Title: [198872] trunk/Source
Revision
198872
Author
[email protected]
Date
2016-03-30 19:03:57 -0700 (Wed, 30 Mar 2016)

Log Message

Web Automation: Add Automation.performKeyboardInteractions
https://bugs.webkit.org/show_bug.cgi?id=155990
<rdar://problem/25426408>

Patch by Brian Burg <[email protected]> on 2016-03-30
Reviewed by Timothy Hatcher.

Source/WebKit2:

Add a command that allows automation to simulate single
key strokes or insertion of an entire string, character
by character.

* UIProcess/Automation/Automation.json: Add new command.
Add a large enumeration of all virtual keys that exist
on a US 109-key keyboard layout. Add an interaction object.

* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::performKeyboardInteractions):
Added. This method validates the incoming key interactions
from the protocol and makes a list of simulated interactions
to perform. If everything validates, then delegate the key
interaction simulations to platform-specific methods.

(WebKit::WebAutomationSession::platformSimulateKeyStroke):
(WebKit::WebAutomationSession::platformSimulateKeySequence):
Add stubs for other platforms.

* UIProcess/Automation/WebAutomationSession.h: Add new
protocol command handler and platform simulation methods.

* UIProcess/Cocoa/WebAutomationSessionCocoa.mm:
(WebKit::WebAutomationSession::platformSimulateKeyStroke):
(WebKit::WebAutomationSession::platformSimulateKeySequence):
These methods implement keyboard simulation for AppKit, used
by the Mac port. In the keystroke case, figure out the AppKit
keyCode for the key as well as any key modifiers that should
be included with the key event. Keep track of sticky modifiers
and update the session state appropriately. In the key sequence
case, split the string into combining character sequences and
send a 'key up/down'. This is a weird way to send non-ASCII
text, so a better alternative should be explored in the future.

Source/WTF:

Add a missing NSEventType declaration.

* wtf/mac/AppKitCompatibilityDeclarations.h:

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (198871 => 198872)


--- trunk/Source/WTF/ChangeLog	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WTF/ChangeLog	2016-03-31 02:03:57 UTC (rev 198872)
@@ -1,3 +1,15 @@
+2016-03-30  Brian Burg  <[email protected]>
+
+        Web Automation: Add Automation.performKeyboardInteractions
+        https://bugs.webkit.org/show_bug.cgi?id=155990
+        <rdar://problem/25426408>
+
+        Reviewed by Timothy Hatcher.
+
+        Add a missing NSEventType declaration.
+
+        * wtf/mac/AppKitCompatibilityDeclarations.h:
+
 2016-03-29  Benjamin Poulain  <[email protected]>
 
         [WTF] Removing a smart pointer from HashTable issues two stores to the same location

Modified: trunk/Source/WTF/wtf/mac/AppKitCompatibilityDeclarations.h (198871 => 198872)


--- trunk/Source/WTF/wtf/mac/AppKitCompatibilityDeclarations.h	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WTF/wtf/mac/AppKitCompatibilityDeclarations.h	2016-03-31 02:03:57 UTC (rev 198872)
@@ -69,6 +69,7 @@
 static const NSEventModifierFlags NSEventModifierFlagFunction = NSFunctionKeyMask;
 static const NSEventModifierFlags NSEventModifierFlagNumericPad = NSNumericPadKeyMask;
 static const NSEventModifierFlags NSEventModifierFlagShift = NSShiftKeyMask;
+static const NSEventModifierFlags NSEventModifierFlagHelp = NSHelpKeyMask;
 
 static const NSEventType NSEventTypeFlagsChanged = NSFlagsChanged;
 static const NSEventType NSEventTypeKeyDown = NSKeyDown;

Modified: trunk/Source/WebKit2/ChangeLog (198871 => 198872)


--- trunk/Source/WebKit2/ChangeLog	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WebKit2/ChangeLog	2016-03-31 02:03:57 UTC (rev 198872)
@@ -1,3 +1,45 @@
+2016-03-30  Brian Burg  <[email protected]>
+
+        Web Automation: Add Automation.performKeyboardInteractions
+        https://bugs.webkit.org/show_bug.cgi?id=155990
+        <rdar://problem/25426408>
+
+        Reviewed by Timothy Hatcher.
+
+        Add a command that allows automation to simulate single
+        key strokes or insertion of an entire string, character
+        by character.
+
+        * UIProcess/Automation/Automation.json: Add new command.
+        Add a large enumeration of all virtual keys that exist
+        on a US 109-key keyboard layout. Add an interaction object.
+
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::performKeyboardInteractions):
+        Added. This method validates the incoming key interactions
+        from the protocol and makes a list of simulated interactions
+        to perform. If everything validates, then delegate the key
+        interaction simulations to platform-specific methods.
+
+        (WebKit::WebAutomationSession::platformSimulateKeyStroke):
+        (WebKit::WebAutomationSession::platformSimulateKeySequence):
+        Add stubs for other platforms.
+
+        * UIProcess/Automation/WebAutomationSession.h: Add new
+        protocol command handler and platform simulation methods.
+
+        * UIProcess/Cocoa/WebAutomationSessionCocoa.mm:
+        (WebKit::WebAutomationSession::platformSimulateKeyStroke):
+        (WebKit::WebAutomationSession::platformSimulateKeySequence):
+        These methods implement keyboard simulation for AppKit, used
+        by the Mac port. In the keystroke case, figure out the AppKit
+        keyCode for the key as well as any key modifiers that should
+        be included with the key event. Keep track of sticky modifiers
+        and update the session state appropriately. In the key sequence
+        case, split the string into combining character sequences and
+        send a 'key up/down'. This is a weird way to send non-ASCII
+        text, so a better alternative should be explored in the future.
+
 2016-03-30  Brady Eidson  <[email protected]>
 
         Make BlobData use ThreadSafeSharedBuffer instead of RawData.

Modified: trunk/Source/WebKit2/UIProcess/Automation/Automation.json (198871 => 198872)


--- trunk/Source/WebKit2/UIProcess/Automation/Automation.json	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WebKit2/UIProcess/Automation/Automation.json	2016-03-31 02:03:57 UTC (rev 198872)
@@ -103,6 +103,88 @@
                 "Meta",
                 "Alt"
             ]
+        },
+        {
+            "id": "VirtualKey",
+            "type": "string",
+            "description": "Enumerates different platform-independent virtual keys on a physical keyboard whose input via keyboard may or may not produce characters.",
+            "enum": [
+                "Shift",
+                "Control",
+                "Alternate",
+                "Meta",
+                "Command",
+                "Cancel",
+                "Help",
+                "Backspace",
+                "Tab",
+                "Clear",
+                "Enter",
+                "Pause",
+                "Escape",
+                "PageUp",
+                "PageDown",
+                "End",
+                "Home",
+                "LeftArrow",
+                "UpArrow",
+                "RightArrow",
+                "DownArrow",
+                "Insert",
+                "Delete",
+                "Space",
+                "Semicolon",
+                "Equals",
+                "Return",
+                "NumberPad0",
+                "NumberPad1",
+                "NumberPad2",
+                "NumberPad3",
+                "NumberPad4",
+                "NumberPad5",
+                "NumberPad6",
+                "NumberPad7",
+                "NumberPad8",
+                "NumberPad9",
+                "NumberPadMultiply",
+                "NumberPadAdd",
+                "NumberPadSeparator",
+                "NumberPadSubtract",
+                "NumberPadDecimal",
+                "NumberPadDivide",
+                "Function1",
+                "Function2",
+                "Function3",
+                "Function4",
+                "Function5",
+                "Function6",
+                "Function7",
+                "Function8",
+                "Function9",
+                "Function10",
+                "Function11",
+                "Function12"
+            ]
+        },
+        {
+            "id": "KeyboardInteractionType",
+            "type": "string",
+            "description": "Enumerates different ways of interacting with a keyboard device. 'InsertByKey' implies that a separate KeyDown and KeyUp event are produced for each combining character sequence, regardless of the actual keystrokes required to produce the character sequence.",
+            "enum": [
+                "KeyPress",
+                "KeyRelease",
+                "InsertByKey"
+            ]
+        },
+        {
+            "id": "KeyboardInteraction",
+            "type": "object",
+            "description": "A single step in a key sequence. A step can contain a key up/down of a virtual key, or a sequence of Unicode code points that are delivered to the page at grapheme cluster boundaries. Either a 'key' or 'text' property must be specified.",
+            "properties": [
+                { "name": "type", "$ref": "KeyboardInteractionType", "description": "The type of interaction to be performed by a step." },
+                { "name": "key", "$ref": "VirtualKey", "optional": true, "description": "A virtual key to be used to perform the specified interaction." },
+                { "name": "text", "type": "string", "optional": true, "description": "A unicode string to be delivered to the page. The sequence of key events is determined by splitting the string at grapheme cluster boundaries." }
+            ]
         }
     ],
     "commands": [
@@ -220,6 +302,14 @@
             ]
         },
         {
+            "name": "performKeyboardInteractions",
+            "description": "Simulates delivering the results of pressing one or more keyboard keys together or successively.",
+            "parameters": [
+                { "name": "handle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context which should recieve key." },
+                { "name": "interactions", "type": "array", "items": { "$ref": "KeyboardInteraction" }, "description": "An ordered list of key sequences to be delivered using native key events." }
+            ]
+        },
+        {
             "name": "resolveChildFrameHandle",
             "description": "Determines the <code>FrameHandle</code> based on the ordinal, name or node handle of a child frame.",
             "parameters": [

Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp (198871 => 198872)


--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp	2016-03-31 02:03:57 UTC (rev 198872)
@@ -708,10 +708,85 @@
 #endif // USE(APPKIT)
 }
 
+void WebAutomationSession::performKeyboardInteractions(ErrorString& errorString, const String& handle, const Inspector::InspectorArray& interactions)
+{
 #if !USE(APPKIT)
+    FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NotImplemented);
+#else
+    WebPageProxy* page = webPageProxyForHandle(handle);
+    if (!page)
+        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
+
+    if (!interactions.length())
+        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+
+    // Validate all of the parameters before performing any interactions with the browsing context under test.
+    Vector<std::function<void()>> actionsToPerform(interactions.length());
+
+    for (auto it = interactions.begin(); it != interactions.end(); ++it) {
+        RefPtr<InspectorObject> interactionObject;
+        if (!it->get()->asObject(interactionObject))
+            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+
+        String interactionTypeString;
+        if (!interactionObject->getString(ASCIILiteral("type"), interactionTypeString))
+            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+        auto interactionType = Inspector::Protocol::parseEnumValueFromString<Inspector::Protocol::Automation::KeyboardInteractionType>(interactionTypeString);
+        if (!interactionType)
+            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+
+        String virtualKeyString;
+        bool foundVirtualKey = interactionObject->getString(ASCIILiteral("key"), virtualKeyString);
+        if (foundVirtualKey) {
+            auto virtualKey = Inspector::Protocol::parseEnumValueFromString<Inspector::Protocol::Automation::VirtualKey>(virtualKeyString);
+            if (!virtualKey)
+                FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+
+            actionsToPerform.append([this, page, interactionType, virtualKey] {
+                platformSimulateKeyStroke(*page, interactionType.value(), virtualKey.value());
+            });
+        }
+
+        String keySequence;
+        bool foundKeySequence = interactionObject->getString(ASCIILiteral("text"), keySequence);
+        if (foundKeySequence) {
+            switch (interactionType.value()) {
+            case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress:
+            case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease:
+                // 'KeyPress' and 'KeyRelease' are meant for a virtual key and are not supported for a string (sequence of codepoints).
+                FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
+
+            case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey:
+                actionsToPerform.append([this, page, keySequence] {
+                    platformSimulateKeySequence(*page, keySequence);
+                });
+                break;
+            }
+        }
+
+        if (!foundVirtualKey && !foundKeySequence)
+            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
+
+        ASSERT(actionsToPerform.size());
+        for (auto& action : actionsToPerform)
+            action();
+    }
+
+#endif // USE(APPKIT)
+}
+
+#if !USE(APPKIT)
 void WebAutomationSession::platformSimulateMouseInteraction(WebKit::WebPageProxy&, const WebCore::IntPoint&, Inspector::Protocol::Automation::MouseInteraction, Inspector::Protocol::Automation::MouseButton, WebEvent::Modifiers)
 {
 }
+
+void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy&, Inspector::Protocol::Automation::KeyboardInteractionType, Inspector::Protocol::Automation::VirtualKey)
+{
+}
+
+void WebAutomationSession::platformSimulateKeySequence(WebPageProxy&, const String&)
+{
+}
 #endif // !USE(APPKIT)
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h (198871 => 198872)


--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h	2016-03-31 02:03:57 UTC (rev 198872)
@@ -104,6 +104,7 @@
     void reloadBrowsingContext(Inspector::ErrorString&, const String&) override;
     void evaluateJavaScriptFunction(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const String& function, const Inspector::InspectorArray& arguments, bool expectsImplicitCallbackArgument, Ref<Inspector::AutomationBackendDispatcherHandler::EvaluateJavaScriptFunctionCallback>&&) override;
     void performMouseInteraction(Inspector::ErrorString&, const String& handle, const Inspector::InspectorObject& requestedPosition, const String& mouseButton, const String& mouseInteraction, const Inspector::InspectorArray& keyModifiers, RefPtr<Inspector::Protocol::Automation::Point>& updatedPosition) override;
+    void performKeyboardInteractions(Inspector::ErrorString&, const String& handle, const Inspector::InspectorArray& interactions) override;
     void resolveChildFrameHandle(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const int* optionalOrdinal, const String* optionalName, const String* optionalNodeHandle, Ref<ResolveChildFrameHandleCallback>&&) override;
     void resolveParentFrameHandle(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, Ref<ResolveParentFrameHandleCallback>&&) override;
     void computeElementLayout(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const bool* optionalScrollIntoViewIfNeeded, const bool* useViewportCoordinates, Ref<Inspector::AutomationBackendDispatcherHandler::ComputeElementLayoutCallback>&&) override;
@@ -137,6 +138,10 @@
 
     // Platform-specific helper methods.
     void platformSimulateMouseInteraction(WebPageProxy&, const WebCore::IntPoint& viewPosition, Inspector::Protocol::Automation::MouseInteraction, Inspector::Protocol::Automation::MouseButton, WebEvent::Modifiers);
+    // Simulates a single virtual key being pressed, such as Control, F-keys, Numpad keys, etc. as allowed by the protocol.
+    void platformSimulateKeyStroke(WebPageProxy&, Inspector::Protocol::Automation::KeyboardInteractionType, Inspector::Protocol::Automation::VirtualKey);
+    // Simulates key presses to produce the codepoints in a string. One or more code points are delivered atomically at grapheme cluster boundaries.
+    void platformSimulateKeySequence(WebPageProxy&, const String&);
 
 #if USE(APPKIT)
     void sendSynthesizedEventsToPage(WebPageProxy&, NSArray *eventsToSend);

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebAutomationSessionCocoa.mm (198871 => 198872)


--- trunk/Source/WebKit2/UIProcess/Cocoa/WebAutomationSessionCocoa.mm	2016-03-31 01:36:30 UTC (rev 198871)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebAutomationSessionCocoa.mm	2016-03-31 02:03:57 UTC (rev 198872)
@@ -33,6 +33,10 @@
 #import <WebCore/PlatformMouseEvent.h>
 #import <objc/runtime.h>
 
+#if USE(APPKIT)
+#import <HIToolbox/Events.h>
+#endif
+
 using namespace WebCore;
 
 namespace WebKit {
@@ -133,6 +137,323 @@
     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
 }
 
+void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
+{
+    // If true, the key's modifier flags should affect other events while pressed down.
+    bool isStickyModifier = false;
+    // The modifiers changed by the virtual key when it is pressed or released.
+    // The mapping from keys to modifiers is specified in the documentation for NSEvent.
+    NSEventModifierFlags changedModifiers = 0;
+    // The likely keyCode for the virtual key as defined in <HIToolbox/Events.h>.
+    int keyCode = 0;
+    // Typical characters produced by the virtual key, if any.
+    NSString *characters;
+
+    // FIXME: this function and the Automation protocol enum should probably adopt key names
+    // from W3C UIEvents standard. For more details: https://w3c.github.io/uievents-code/
+
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::Shift:
+        isStickyModifier = true;
+        changedModifiers |= NSEventModifierFlagShift;
+        keyCode = kVK_Shift;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Control:
+        isStickyModifier = true;
+        changedModifiers |= NSEventModifierFlagControl;
+        keyCode = kVK_Control;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Alternate:
+        isStickyModifier = true;
+        changedModifiers |= NSEventModifierFlagOption;
+        keyCode = kVK_Option;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Meta:
+        // The 'meta' key does not exist on Apple keyboards and is usually
+        // mapped to the Command key when using third-party keyboards.
+    case Inspector::Protocol::Automation::VirtualKey::Command:
+        isStickyModifier = true;
+        changedModifiers |= NSEventModifierFlagCommand;
+        keyCode = kVK_Command;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Help:
+        changedModifiers |= NSEventModifierFlagHelp | NSEventModifierFlagFunction;
+        keyCode = kVK_Help;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Backspace:
+        keyCode = kVK_Delete;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Tab:
+        keyCode = kVK_Tab;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Clear:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadClear;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Enter:
+        keyCode = kVK_ANSI_KeypadEnter;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Pause:
+        // The 'pause' key does not exist on Apple keyboards and has no keycode.
+        // The semantics are unclear so just abort and do nothing.
+        return;
+    case Inspector::Protocol::Automation::VirtualKey::Cancel:
+        // The 'cancel' key does not exist on Apple keyboards and has no keycode.
+        // According to the internet its functionality is similar to 'Escape'.
+    case Inspector::Protocol::Automation::VirtualKey::Escape:
+        keyCode = kVK_Escape;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::PageUp:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_PageUp;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::PageDown:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_PageDown;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::End:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_End;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Home:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_Home;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
+        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
+        keyCode = kVK_LeftArrow;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::UpArrow:
+        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
+        keyCode = kVK_UpArrow;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::RightArrow:
+        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
+        keyCode = kVK_RightArrow;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::DownArrow:
+        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
+        keyCode = kVK_DownArrow;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Insert:
+        // The 'insert' key does not exist on Apple keyboards and has no keycode.
+        // The semantics are unclear so just abort and do nothing.
+        return;
+    case Inspector::Protocol::Automation::VirtualKey::Delete:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_ForwardDelete;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Space:
+        keyCode = kVK_Space;
+        characters = @" ";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Semicolon:
+        keyCode = kVK_ANSI_Semicolon;
+        characters = @";";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Equals:
+        keyCode = kVK_ANSI_Equal;
+        characters = @"=";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Return:
+        keyCode = kVK_Return;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad0;
+        characters = @"0";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad1;
+        characters = @"1";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad2;
+        characters = @"2";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad3;
+        characters = @"3";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad4;
+        characters = @"4";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad5;
+        characters = @"5";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad6;
+        characters = @"6";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad7;
+        characters = @"7";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad8;
+        characters = @"8";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_Keypad9;
+        characters = @"9";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadMultiply;
+        characters = @"*";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadPlus;
+        characters = @"+";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadMinus;
+        characters = @"-";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        // The 'Separator' key is only present on a few international keyboards.
+        // It is usually mapped to the same character as Decimal ('.' or ',').
+        FALLTHROUGH;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadDecimal;
+        // FIXME: this might be locale-dependent. See the above comment.
+        characters = @".";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
+        changedModifiers |= NSEventModifierFlagNumericPad;
+        keyCode = kVK_ANSI_KeypadDivide;
+        characters = @"/";
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function1:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F1;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function2:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F2;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function3:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F3;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function4:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F4;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function5:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F5;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function6:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F6;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function7:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F7;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function8:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F8;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function9:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F9;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function10:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F10;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function11:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F11;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Function12:
+        changedModifiers |= NSEventModifierFlagFunction;
+        keyCode = kVK_F12;
+        break;
+    }
+
+    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
+
+    ASSERT(isStickyModifier || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
+
+    NSEventModifierFlags existingModifiers = [NSEvent modifierFlags];
+    NSEventModifierFlags updatedModifiers = 0;
+    NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
+    NSWindow *window = page.platformWindow();
+    NSInteger windowNumber = window.windowNumber;
+    NSPoint eventPosition = NSMakePoint(0, window.frame.size.height);
+
+    switch (interaction) {
+    case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress: {
+        NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyDown;
+        updatedModifiers = existingModifiers | changedModifiers;
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
+        break;
+    }
+    case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
+        NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyUp;
+        updatedModifiers = existingModifiers & ~changedModifiers;
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
+        break;
+    }
+    case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
+        // Sticky modifiers should either be 'KeyPress' or 'KeyRelease'.
+        ASSERT(!isStickyModifier);
+        if (isStickyModifier)
+            return;
+
+        updatedModifiers = existingModifiers | changedModifiers;
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyDown location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyUp location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
+        break;
+    }
+    }
+
+    sendSynthesizedEventsToPage(page, eventsToBeSent.get());
+}
+
+void WebAutomationSession::platformSimulateKeySequence(WebPageProxy& page, const String& keySequence)
+{
+    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
+
+    // Split the text into combining character sequences and send each separately.
+    // This has no similarity to how keyboards work when inputting complex text.
+    // This command is more similar to the 'insertText:' editing command, except
+    // that this emits keyup/keydown/keypress events for roughly each character.
+    // This API should move more towards that direction in the future.
+    NSString *text = keySequence;
+
+    NSEventModifierFlags modifiers = [NSEvent modifierFlags];
+    NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
+    NSWindow *window = page.platformWindow();
+    NSInteger windowNumber = window.windowNumber;
+    NSPoint eventPosition = NSMakePoint(0, window.frame.size.height);
+
+    [text enumerateSubstringsInRange:NSMakeRange(0, text.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyDown location:eventPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:substring charactersIgnoringModifiers:substring isARepeat:NO keyCode:0]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyUp location:eventPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:substring charactersIgnoringModifiers:substring isARepeat:NO keyCode:0]];
+    }];
+
+    sendSynthesizedEventsToPage(page, eventsToBeSent.get());
+}
+
 #endif // USE(APPKIT)
 
 } // namespace WebKit
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to