[iOS] Shift + Tab does not focus previous field
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Nov 2018 19:15:28 +0000 (19:15 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Nov 2018 19:15:28 +0000 (19:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191596
<rdar://problem/45892053>

Reviewed by Wenson Hsieh.

Source/WebKit:

Wire up the the tab and shift + tab key commands to the WKWebView/WKContentView's
action forwarding mechanism. Also rename -_prevAccessoryTab to -_previousAccessoryTab.

* Platform/spi/ios/UIKitSPI.h: Add more SPI.
* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView canPerformActionForWebView:withSender:]):
(-[WKContentView keyCommands]):
(-[WKContentView _nextAccessoryTabForWebView:]): Added.
(-[WKContentView _previousAccessoryTabForWebView:]): Added.
(-[WKContentView _nextAccessoryTab:]): Deleted.
(-[WKContentView _prevAccessoryTab:]): Deleted.

Tools:

Add infrastructure to support testing a key down event with modifiers by creating
and dispatching a UIEvent. This infrastructure replaces the previous mechanism in
Tools/WebKitTestRunner/ios/HIDEventGenerator.mm to generate a IOHIDEvent for a
keydown as it did not support creating an event with modifier key state that would
be recognized by UIKit.

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::keyDown):
(WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
(WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
Add function uiController.keyDown() that takes a character that represents a keyboard key
and an array of modifier keys. The behavior of this function is analogous to eventSender.keyDown().
Remove functions uiController.keyDownUsingHardwareKeyboard() and uiController.keyUpUsingHardwareKeyboard()
as the former is replaced by uiController.keyDown() and the latter was never used.

* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::keyDown): Added.
(WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.
(WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/ios/HIDEventGenerator.h:
* WebKitTestRunner/ios/HIDEventGenerator.mm:
(createHIDKeyDownEvent): Added.
(-[HIDEventGenerator keyDown:completionBlock:]): Deleted.
(-[HIDEventGenerator keyUp:completionBlock:]): Deleted.
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::arrayLength): Added.
(WTR::parseModifier): Added.
(WTR::parseModifierArray): Added.
(WTR::UIScriptController::keyDown):
(WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
(WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.

LayoutTests:

Add tests to ensure that we do not regress the tab and shift + tab key commands.

* fast/events/ios/focus-tab-next-field-expected.txt: Added.
* fast/events/ios/focus-tab-next-field.html: Added.
* fast/events/ios/focus-tab-previous-field-expected.txt: Added.
* fast/events/ios/focus-tab-previous-field.html: Added.
* platform/ios-wk1/TestExpectations: Skip test focus-tab-previous-field.html in Legacy WebKit as
we do not support testing key commands with modifier keys.
* resources/ui-helper.js:
(window.UIHelper.keyDown): Updated to support taking an optional array of modifiers (defaults: [])
and use the new infrastructure to generate a key down event for the specified character.

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/ios/focus-tab-next-field-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/ios/focus-tab-next-field.html [new file with mode: 0644]
LayoutTests/fast/events/ios/focus-tab-previous-field-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/ios/focus-tab-previous-field.html [new file with mode: 0644]
LayoutTests/platform/ios-wk1/TestExpectations
LayoutTests/resources/ui-helper.js
Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/ios/UIKitSPI.h
Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/ios/HIDEventGenerator.h
Tools/WebKitTestRunner/ios/HIDEventGenerator.mm
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

index db408ed..cb53c4b 100644 (file)
@@ -1,3 +1,23 @@
+2018-11-15  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Shift + Tab does not focus previous field
+        https://bugs.webkit.org/show_bug.cgi?id=191596
+        <rdar://problem/45892053>
+
+        Reviewed by Wenson Hsieh.
+
+        Add tests to ensure that we do not regress the tab and shift + tab key commands.
+
+        * fast/events/ios/focus-tab-next-field-expected.txt: Added.
+        * fast/events/ios/focus-tab-next-field.html: Added.
+        * fast/events/ios/focus-tab-previous-field-expected.txt: Added.
+        * fast/events/ios/focus-tab-previous-field.html: Added.
+        * platform/ios-wk1/TestExpectations: Skip test focus-tab-previous-field.html in Legacy WebKit as
+        we do not support testing key commands with modifier keys.
+        * resources/ui-helper.js:
+        (window.UIHelper.keyDown): Updated to support taking an optional array of modifiers (defaults: [])
+        and use the new infrastructure to generate a key down event for the specified character.
+
 2018-11-15  Youenn Fablet  <youenn@apple.com>
 
         Update RTCPeerConnection JS built-ins to be closer to specWe
diff --git a/LayoutTests/fast/events/ios/focus-tab-next-field-expected.txt b/LayoutTests/fast/events/ios/focus-tab-next-field-expected.txt
new file mode 100644 (file)
index 0000000..3f54c82
--- /dev/null
@@ -0,0 +1,5 @@
+Tests that pressing the tab key in a focused field focuses the next field in tab order.
+
+PASS focused the second field.
+
+
diff --git a/LayoutTests/fast/events/ios/focus-tab-next-field.html b/LayoutTests/fast/events/ios/focus-tab-next-field.html
new file mode 100644 (file)
index 0000000..c06daa4
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/ui-helper.js"></script>
+</head>
+<body>
+<p>Tests that pressing the tab key in a focused field focuses the next field in tab order.</p>
+<p id="result">FAIL did not focus the second field.</p>
+<input type="text" id="firstField"><input type="text" id="secondField" onfocus="passed()">
+<script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+async function runTest()
+{
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    await UIHelper.activateFormControl(document.getElementById("firstField"));
+    await UIHelper.keyDown("\t");
+}
+
+function passed()
+{
+    document.getElementById("result").textContent = "PASS focused the second field.";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/ios/focus-tab-previous-field-expected.txt b/LayoutTests/fast/events/ios/focus-tab-previous-field-expected.txt
new file mode 100644 (file)
index 0000000..604a76a
--- /dev/null
@@ -0,0 +1,5 @@
+Tests that pressing shift + tab in a focused field focuses the previous field in tab order.
+
+PASS focused the first field.
+
+
diff --git a/LayoutTests/fast/events/ios/focus-tab-previous-field.html b/LayoutTests/fast/events/ios/focus-tab-previous-field.html
new file mode 100644 (file)
index 0000000..93f7182
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/ui-helper.js"></script>
+</head>
+<body>
+<p>Tests that pressing shift + tab in a focused field focuses the previous field in tab order.</p>
+<p id="result">FAIL did not focus the first field.</p>
+<input type="text" id="firstField" onfocus="passed()"><input type="text" id="secondField" autofocus>
+<script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+async function runTest()
+{
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    await UIHelper.activateFormControl(document.getElementById("secondField"));
+    await UIHelper.keyDown("\t", ["shiftKey"]);
+}
+
+function passed()
+{
+    document.getElementById("result").textContent = "PASS focused the first field.";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+runTest();
+</script>
+</body>
+</html>
index 2fbe7d5..6cf9f28 100644 (file)
@@ -31,6 +31,9 @@ imported/w3c/web-platform-tests/pointerevents [ Skip ]
 fast/forms/datalist [ WontFix ]
 imported/w3c/web-platform-tests/html/semantics/forms/the-datalist-element [ WontFix ]
 
+# No support for testing key commands with modifiers in WK1
+fast/events/ios/focus-tab-previous-field.html [ Skip ]
+
 # <input type=color> is not supported in WebKit1 on iOS.
 fast/forms/color/input-color-onchange-event.html [ Failure ]
 fast/forms/color/input-color-readonly.html [ Failure ]
index f096591..1a5f4b7 100644 (file)
@@ -54,7 +54,7 @@ window.UIHelper = class UIHelper {
         return UIHelper.activateAt(x, y);
     }
 
-    static keyDown(key)
+    static keyDown(key, modifiers=[])
     {
         if (!this.isWebKit2() || !this.isIOS()) {
             eventSender.keyDown(key);
@@ -62,10 +62,7 @@ window.UIHelper = class UIHelper {
         }
 
         return new Promise((resolve) => {
-            testRunner.runUIScript(`
-                uiController.keyDownUsingHardwareKeyboard("downArrow", function() {
-                    uiController.uiScriptComplete("Done");
-                });`, resolve);
+            testRunner.runUIScript(`uiController.keyDown("${key}", ${JSON.stringify(modifiers)});`, resolve);
         });
     }
 
index b12a132..6f23936 100644 (file)
@@ -1,3 +1,25 @@
+2018-11-15  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Shift + Tab does not focus previous field
+        https://bugs.webkit.org/show_bug.cgi?id=191596
+        <rdar://problem/45892053>
+
+        Reviewed by Wenson Hsieh.
+
+        Wire up the the tab and shift + tab key commands to the WKWebView/WKContentView's
+        action forwarding mechanism. Also rename -_prevAccessoryTab to -_previousAccessoryTab.
+
+        * Platform/spi/ios/UIKitSPI.h: Add more SPI.
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView canPerformActionForWebView:withSender:]):
+        (-[WKContentView keyCommands]):
+        (-[WKContentView _nextAccessoryTabForWebView:]): Added.
+        (-[WKContentView _previousAccessoryTabForWebView:]): Added.
+        (-[WKContentView _nextAccessoryTab:]): Deleted.
+        (-[WKContentView _prevAccessoryTab:]): Deleted.
+
 2018-11-15  Keith Rollin  <krollin@apple.com>
 
         Delete old .xcfilelist files
index f295b13..9eefb97 100644 (file)
@@ -158,7 +158,8 @@ typedef NS_ENUM(NSInteger, UIPreviewItemType) {
 @end
 
 WTF_EXTERN_C_BEGIN
-typedef struct __IOHIDEvent * IOHIDEventRef;
+typedef struct __IOHIDEvent* IOHIDEventRef;
+typedef struct __GSKeyboard* GSKeyboardRef;
 WTF_EXTERN_C_END
 
 @interface UIApplication ()
@@ -168,6 +169,7 @@ WTF_EXTERN_C_END
 - (BOOL)isSuspendedUnderLock;
 - (void)_enqueueHIDEvent:(IOHIDEventRef)event;
 - (void)_handleHIDEvent:(IOHIDEventRef)event;
+- (void)handleKeyUIEvent:(UIEvent *)event;
 @end
 
 typedef NS_ENUM(NSInteger, UIDatePickerPrivateMode)  {
@@ -1023,8 +1025,10 @@ typedef NSInteger UICompositingMode;
 @end
 
 @interface UIPhysicalKeyboardEvent ()
-@property (nonatomic, readonly) UIKeyboardInputFlags _inputFlags;
++ (UIPhysicalKeyboardEvent *)_eventWithInput:(NSString *)input inputFlags:(UIKeyboardInputFlags)flags;
+- (void)_setHIDEvent:(IOHIDEventRef)event keyboard:(GSKeyboardRef)gsKeyboard;
 - (UIPhysicalKeyboardEvent *)_cloneEvent NS_RETURNS_RETAINED;
+@property (nonatomic, readonly) UIKeyboardInputFlags _inputFlags;
 @property (nonatomic, readonly) CFIndex _keyCode;
 @end
 
index 5c3dc34..c266d6e 100644 (file)
@@ -151,6 +151,9 @@ class URL;
 - (void)_transliterateChinese:(id)sender;
 - (void)replace:(id)sender;
 
+- (void)_nextAccessoryTab:(id)sender;
+- (void)_previousAccessoryTab:(id)sender;
+
 - (void)_incrementFocusPreservationCount;
 - (void)_decrementFocusPreservationCount;
 - (void)_resetFocusPreservationCount;
index 3bd1e25..c48c39d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -114,6 +114,8 @@ typedef std::pair<WebKit::InteractionInformationRequest, InteractionInformationC
     M(_share) \
     M(_showTextStyleOptions) \
     M(_transliterateChinese) \
+    M(_nextAccessoryTab) \
+    M(_previousAccessoryTab) \
     M(copy) \
     M(cut) \
     M(paste) \
index 52cba4f..21d3177 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -2440,7 +2440,12 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
 }
 
 - (BOOL)canPerformActionForWebView:(SEL)action withSender:(id)sender
-{        
+{
+    if (action == @selector(_nextAccessoryTab:))
+        return hasAssistedNode(_assistedNodeInformation) && _assistedNodeInformation.hasNextNode;
+    if (action == @selector(_previousAccessoryTab:))
+        return hasAssistedNode(_assistedNodeInformation) && _assistedNodeInformation.hasPreviousNode;
+
     auto editorState = _page->editorState();
     if (action == @selector(_showTextStyleOptions:))
         return editorState.isContentRichlyEditable && editorState.selectionIsRange && !_showingTextStyleOptions;
@@ -3218,18 +3223,18 @@ static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoi
         return nil;
 
     static NSArray* editableKeyCommands = [@[
-       [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
-       [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
+        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
+        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_previousAccessoryTab:)]
     ] retain];
     return editableKeyCommands;
 }
 
-- (void)_nextAccessoryTab:(id)sender
+- (void)_nextAccessoryTabForWebView:(id)sender
 {
     [self accessoryTab:YES];
 }
 
-- (void)_prevAccessoryTab:(id)sender
+- (void)_previousAccessoryTabForWebView:(id)sender
 {
     [self accessoryTab:NO];
 }
index a354182..787281c 100644 (file)
@@ -1,3 +1,45 @@
+2018-11-15  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Shift + Tab does not focus previous field
+        https://bugs.webkit.org/show_bug.cgi?id=191596
+        <rdar://problem/45892053>
+
+        Reviewed by Wenson Hsieh.
+
+        Add infrastructure to support testing a key down event with modifiers by creating
+        and dispatching a UIEvent. This infrastructure replaces the previous mechanism in
+        Tools/WebKitTestRunner/ios/HIDEventGenerator.mm to generate a IOHIDEvent for a
+        keydown as it did not support creating an event with modifier key state that would
+        be recognized by UIKit.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::keyDown):
+        (WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
+        (WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        Add function uiController.keyDown() that takes a character that represents a keyboard key
+        and an array of modifier keys. The behavior of this function is analogous to eventSender.keyDown().
+        Remove functions uiController.keyDownUsingHardwareKeyboard() and uiController.keyUpUsingHardwareKeyboard()
+        as the former is replaced by uiController.keyDown() and the latter was never used.
+
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::keyDown): Added.
+        (WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.
+        (WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/ios/HIDEventGenerator.h:
+        * WebKitTestRunner/ios/HIDEventGenerator.mm:
+        (createHIDKeyDownEvent): Added.
+        (-[HIDEventGenerator keyDown:completionBlock:]): Deleted.
+        (-[HIDEventGenerator keyUp:completionBlock:]): Deleted.
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::arrayLength): Added.
+        (WTR::parseModifier): Added.
+        (WTR::parseModifierArray): Added.
+        (WTR::UIScriptController::keyDown):
+        (WTR::UIScriptController::keyDownUsingHardwareKeyboard): Deleted.
+        (WTR::UIScriptController::keyUpUsingHardwareKeyboard): Deleted.
+
 2018-11-15  Keith Rollin  <krollin@apple.com>
 
         Delete old .xcfilelist files
index d41686d..ae46df6 100644 (file)
@@ -151,11 +151,7 @@ void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef characte
 {
 }
 
-void UIScriptController::keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
-{
-}
-
-void UIScriptController::keyUpUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
+void UIScriptController::keyDown(JSStringRef, JSValueRef)
 {
 }
 
index 52d330a..e001807 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,14 @@ enum DeviceOrientation {
     "landscape-right"
 };
 
+enum ModifierKey {
+    "alt",
+    "capsLock",
+    "ctrl",
+    "meta", // Command key on Mac and iOS
+    "shift"
+};
+
 interface UIScriptController {
 
     void doAsyncTask(object callback); // Used to test the harness.
@@ -60,8 +68,8 @@ interface UIScriptController {
 
     void enterText(DOMString text);
     void typeCharacterUsingHardwareKeyboard(DOMString character, object callback);
-    void keyDownUsingHardwareKeyboard(DOMString character, object callback);
-    void keyUpUsingHardwareKeyboard(DOMString character, object callback);
+
+    void keyDown(DOMString character, object modifierArray);
 
     // eventsJSON describes a series of user events in JSON form. For the keys, see HIDEventGenerator.mm.
     // For example, this JSON describes a touch down followed by a touch up (i.e. a single tap).
index 4dcb4a6..67ca799 100644 (file)
@@ -296,11 +296,7 @@ void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef, JSValue
 {
 }
 
-void UIScriptController::keyUpUsingHardwareKeyboard(JSStringRef, JSValueRef)
-{
-}
-
-void UIScriptController::keyDownUsingHardwareKeyboard(JSStringRef, JSValueRef)
+void UIScriptController::keyDown(JSStringRef, JSValueRef)
 {
 }
 
index ebbf548..53eddc8 100644 (file)
@@ -89,8 +89,8 @@ public:
 
     void enterText(JSStringRef);
     void typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
-    void keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
-    void keyUpUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
+
+    void keyDown(JSStringRef character, JSValueRef modifierArray);
 
     void keyboardAccessoryBarNext();
     void keyboardAccessoryBarPrevious();
index e7a9e98..16f5daa 100644 (file)
@@ -26,6 +26,7 @@
 #import "UIKitSPI.h"
 
 #import <CoreGraphics/CGGeometry.h>
+#import <wtf/RetainPtr.h>
 
 // Keys for sendEventStream:completionBlock:.
 extern NSString* const TopLevelEventInfoKey;
@@ -68,6 +69,8 @@ extern NSString* const HIDEventPhaseCanceled;
 
 extern NSUInteger const HIDMaxTouchCount;
 
+RetainPtr<IOHIDEventRef> createHIDKeyDownEvent(NSString *, uint64_t timestamp);
+
 @interface HIDEventGenerator : NSObject
 
 + (HIDEventGenerator *)sharedHIDEventGenerator;
@@ -105,7 +108,5 @@ extern NSUInteger const HIDMaxTouchCount;
 
 // Keyboard
 - (void)keyPress:(NSString *)character completionBlock:(void (^)(void))completionBlock;
-- (void)keyDown:(NSString *)character completionBlock:(void (^)(void))completionBlock;
-- (void)keyUp:(NSString *)character completionBlock:(void (^)(void))completionBlock;
 
 @end
index 4c842b8..71612fc 100644 (file)
@@ -33,7 +33,6 @@
 #import <wtf/Assertions.h>
 #import <wtf/BlockPtr.h>
 #import <wtf/Optional.h>
-#import <wtf/RetainPtr.h>
 #import <wtf/SoftLinking.h>
 
 SOFT_LINK_PRIVATE_FRAMEWORK(BackBoardServices)
@@ -199,8 +198,6 @@ static void delayBetweenMove(int eventIndex, double elapsed)
     [super dealloc];
 }
 
-
-
 - (void)_sendIOHIDKeyboardEvent:(uint64_t)timestamp usage:(uint32_t)usage isKeyDown:(bool)isKeyDown
 {
     RetainPtr<IOHIDEventRef> eventRef = adoptCF(IOHIDEventCreateKeyboardEvent(kCFAllocatorDefault,
@@ -976,25 +973,12 @@ static inline uint32_t hidUsageCodeForCharacter(NSString *key)
     return 0;
 }
 
-- (void)keyPress:(NSString *)character completionBlock:(void (^)(void))completionBlock
+RetainPtr<IOHIDEventRef> createHIDKeyDownEvent(NSString *character, uint64_t timestamp)
 {
-    bool shouldWrapWithShift = shouldWrapWithShiftKeyEventForCharacter(character);
-    uint32_t usage = hidUsageCodeForCharacter(character);
-    uint64_t absoluteMachTime = mach_absolute_time();
-
-    if (shouldWrapWithShift)
-        [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:kHIDUsage_KeyboardLeftShift isKeyDown:true];
-
-    [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:usage isKeyDown:true];
-    [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:usage isKeyDown:false];
-
-    if (shouldWrapWithShift)
-        [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:kHIDUsage_KeyboardLeftShift isKeyDown:false];
-
-    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
+    return adoptCF(IOHIDEventCreateKeyboardEvent(kCFAllocatorDefault, timestamp, kHIDPage_KeyboardOrKeypad, hidUsageCodeForCharacter(character), true /* key down */, kIOHIDEventOptionNone));
 }
 
-- (void)keyDown:(NSString *)character completionBlock:(void (^)(void))completionBlock
+- (void)keyPress:(NSString *)character completionBlock:(void (^)(void))completionBlock
 {
     bool shouldWrapWithShift = shouldWrapWithShiftKeyEventForCharacter(character);
     uint32_t usage = hidUsageCodeForCharacter(character);
@@ -1004,16 +988,6 @@ static inline uint32_t hidUsageCodeForCharacter(NSString *key)
         [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:kHIDUsage_KeyboardLeftShift isKeyDown:true];
 
     [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:usage isKeyDown:true];
-
-    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
-}
-
-- (void)keyUp:(NSString *)character completionBlock:(void (^)(void))completionBlock
-{
-    bool shouldWrapWithShift = shouldWrapWithShiftKeyEventForCharacter(character);
-    uint32_t usage = hidUsageCodeForCharacter(character);
-    uint64_t absoluteMachTime = mach_absolute_time();
-
     [self _sendIOHIDKeyboardEvent:absoluteMachTime usage:usage isKeyDown:false];
 
     if (shouldWrapWithShift)
index ffea7a0..610fa20 100644 (file)
 #import <WebCore/FloatRect.h>
 #import <WebKit/WKWebViewPrivate.h>
 #import <WebKit/WebKit.h>
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_FRAMEWORK(UIKit)
+SOFT_LINK_CLASS(UIKit, UIPhysicalKeyboardEvent)
+
+@interface UIPhysicalKeyboardEvent (UIPhysicalKeyboardEventHack)
+@property (nonatomic, assign) NSInteger _modifierFlags;
+@end
 
 namespace WTR {
 
@@ -340,28 +348,75 @@ void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef characte
     }];
 }
 
-void UIScriptController::keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
+static unsigned arrayLength(JSContextRef context, JSObjectRef array)
 {
-    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+    auto lengthString = adopt(JSStringCreateWithUTF8CString("length"));
+    if (auto lengthValue = JSObjectGetProperty(context, array, lengthString.get(), nullptr))
+        return static_cast<unsigned>(JSValueToNumber(context, lengthValue, nullptr));
+    return 0;
+}
 
-    // Assumes that the keyboard is already shown.
-    [[HIDEventGenerator sharedHIDEventGenerator] keyDown:toWTFString(toWK(character)) completionBlock:^{
-        if (!m_context)
-            return;
-        m_context->asyncTaskComplete(callbackID);
-    }];
+static UIKeyModifierFlags parseModifier(JSStringRef modifier)
+{
+    if (JSStringIsEqualToUTF8CString(modifier, "altKey"))
+        return UIKeyModifierAlternate;
+    if (JSStringIsEqualToUTF8CString(modifier, "capsLockKey"))
+        return UIKeyModifierAlphaShift;
+    if (JSStringIsEqualToUTF8CString(modifier, "ctrlKey"))
+        return UIKeyModifierControl;
+    if (JSStringIsEqualToUTF8CString(modifier, "metaKey"))
+        return UIKeyModifierCommand;
+    if (JSStringIsEqualToUTF8CString(modifier, "shiftKey"))
+        return UIKeyModifierShift;
+    return 0;
 }
 
-void UIScriptController::keyUpUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
+static UIKeyModifierFlags parseModifierArray(JSContextRef context, JSValueRef arrayValue)
 {
-    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+    if (!arrayValue)
+        return 0;
 
-    // Assumes that the keyboard is already shown.
-    [[HIDEventGenerator sharedHIDEventGenerator] keyUp:toWTFString(toWK(character)) completionBlock:^{
-        if (!m_context)
-            return;
-        m_context->asyncTaskComplete(callbackID);
-    }];
+    // The value may either be a string with a single modifier or an array of modifiers.
+    if (JSValueIsString(context, arrayValue)) {
+        auto string = adopt(JSValueToStringCopy(context, arrayValue, nullptr));
+        return parseModifier(string.get());
+    }
+
+    if (!JSValueIsObject(context, arrayValue))
+        return 0;
+    JSObjectRef array = const_cast<JSObjectRef>(arrayValue);
+    unsigned length = arrayLength(context, array);
+    UIKeyModifierFlags modifiers = 0;
+    for (unsigned i = 0; i < length; ++i) {
+        JSValueRef exception = nullptr;
+        JSValueRef value = JSObjectGetPropertyAtIndex(context, array, i, &exception);
+        if (exception)
+            continue;
+        auto string = adopt(JSValueToStringCopy(context, value, &exception));
+        if (exception)
+            continue;
+        modifiers |= parseModifier(string.get());
+    }
+    return modifiers;
+}
+
+void UIScriptController::keyDown(JSStringRef character, JSValueRef modifierArray)
+{
+    // Character can be either a single Unicode code point or the name of a special key (e.g. "downArrow").
+    // createHIDKeyEvent() knows how to map these special keys to the appropriate keycode.
+    //
+    // FIXME: The UIEvent input string for special keys (e.g. "downArrow") should either be a UIKeyInput*
+    // string constant or an ASCII control character. In practice the input string for a special key is
+    // ambiguious (e.g. F5 and F6 have the same string - the ASCII DLE character) and hence it is effectively
+    // ignored in favor of key identification by keycode. So, we just take the empty string as the input string
+    // for a special key.
+    String inputString = toWTFString(toWK(character));
+    String uiEventInputString = inputString.length() > 1 ? emptyString() : inputString;
+    auto *keyboardEvent = [getUIPhysicalKeyboardEventClass() _eventWithInput:uiEventInputString inputFlags:(UIKeyboardInputFlags)0];
+    keyboardEvent._modifierFlags = parseModifierArray(m_context->jsContext(), modifierArray);
+    auto hidEvent = createHIDKeyDownEvent(inputString, keyboardEvent.timestamp);
+    [keyboardEvent _setHIDEvent:hidEvent.get() keyboard:nullptr];
+    [[UIApplication sharedApplication] handleKeyUIEvent:keyboardEvent];
 }
 
 void UIScriptController::dismissFormAccessoryView()