[Cocoa] Web Automation: non-sticky virtual keys like 'left arrow' don't work properly
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Mar 2017 15:54:32 +0000 (15:54 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Mar 2017 15:54:32 +0000 (15:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169733
<rdar://problem/30162608>

Reviewed by Joseph Pecoraro.

There were several issues that caused certain virtual keys to not work correctly.
When a virtual key like 'left arrow' was dispatched as a keydown event, it was
ultimately being translated into an insertText: command instead of moveLeft:.

 - The automation browser window was not properly made key window and active, so
   AppKit never tried to match the NSEvent as a key equivalent. That code path
   must be taken in this case, as it translates arrow keys into command selectors.

 - AppKit relies on its own private use area (PUA) unicode characters to encode
   control keys that do not affect key modifier state, like the arrow keys.
   Since these PUA characters were not being used as the 'characters' of the
   NSEvents we synthesize, the events are treated as unknown and AppKit falls
   back to inserting the codepoint as uninterpreted text.

 - The Mac implementation of platformSimulateKeyStroke did not allow non-sticky
   virtual keys to use the 'InsertByKey' interaction which sends keydown+keyup.
   This is a programming mistake that causes such inputs to assert in debug builds
   and bail out to do nothing in non-debug builds.

 - A few simulated virtual keys that are matched to key equivalents did not properly set
   'charactersIgnoringModifiers' on NSEvents, which may use the wrong editing command.

* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::performKeyboardInteractions):
Fix this guard so that we actually call into key event synthesis code for iOS.

* UIProcess/Automation/WebAutomationSession.h: Add declarations.

* UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm:
(WebKit::WebAutomationSession::charCodeForVirtualKey): Moved from iOS implementation.
(WebKit::WebAutomationSession::charCodeIgnoringModifiersForVirtualKey): Added.
There are only a few special cases for now. We will probably need to hardcode
the decomposition for other ASCII characters so the expected DOM events are fired
when entering a shifted character (i.e., 'A' should be 'Shift'+'a', not 'A').

* UIProcess/Automation/ios/WebAutomationSessionIOS.mm:
(WebKit::WebAutomationSession::platformSimulateKeyStroke):
Use charCodeIgnoringModifiersForVirtualKey().

* UIProcess/Automation/mac/WebAutomationSessionMac.mm:
(WebKit::WebAutomationSession::sendSynthesizedEventsToPage): use -becomeKeyWindow.
(WebKit::keyHasStickyModifier): Added.
(WebKit::keyCodeForVirtualKey): Added.
(WebKit::eventModifierFlagsForVirtualKey):Added.
(WebKit::WebAutomationSession::platformSimulateKeyStroke):
Separately compute key stickiness, keyCode, event modifier, and charCode for
the simulated keystroke. The code to compute charCode is now shared between
iOS and macOS since the PUA characters are the same for both AppKit and UIKit.

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/Automation/WebAutomationSession.h
Source/WebKit2/UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm
Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm
Source/WebKit2/UIProcess/Automation/mac/WebAutomationSessionMac.mm

index ecb8343..bcae635 100644 (file)
@@ -1,3 +1,60 @@
+2017-03-19  Brian Burg  <bburg@apple.com>
+
+        [Cocoa] Web Automation: non-sticky virtual keys like 'left arrow' don't work properly
+        https://bugs.webkit.org/show_bug.cgi?id=169733
+        <rdar://problem/30162608>
+
+        Reviewed by Joseph Pecoraro.
+
+        There were several issues that caused certain virtual keys to not work correctly.
+        When a virtual key like 'left arrow' was dispatched as a keydown event, it was
+        ultimately being translated into an insertText: command instead of moveLeft:.
+
+         - The automation browser window was not properly made key window and active, so
+           AppKit never tried to match the NSEvent as a key equivalent. That code path
+           must be taken in this case, as it translates arrow keys into command selectors.
+
+         - AppKit relies on its own private use area (PUA) unicode characters to encode
+           control keys that do not affect key modifier state, like the arrow keys.
+           Since these PUA characters were not being used as the 'characters' of the
+           NSEvents we synthesize, the events are treated as unknown and AppKit falls
+           back to inserting the codepoint as uninterpreted text.
+
+         - The Mac implementation of platformSimulateKeyStroke did not allow non-sticky
+           virtual keys to use the 'InsertByKey' interaction which sends keydown+keyup.
+           This is a programming mistake that causes such inputs to assert in debug builds
+           and bail out to do nothing in non-debug builds.
+
+         - A few simulated virtual keys that are matched to key equivalents did not properly set
+           'charactersIgnoringModifiers' on NSEvents, which may use the wrong editing command.
+
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::performKeyboardInteractions):
+        Fix this guard so that we actually call into key event synthesis code for iOS.
+
+        * UIProcess/Automation/WebAutomationSession.h: Add declarations.
+
+        * UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm:
+        (WebKit::WebAutomationSession::charCodeForVirtualKey): Moved from iOS implementation.
+        (WebKit::WebAutomationSession::charCodeIgnoringModifiersForVirtualKey): Added.
+        There are only a few special cases for now. We will probably need to hardcode
+        the decomposition for other ASCII characters so the expected DOM events are fired
+        when entering a shifted character (i.e., 'A' should be 'Shift'+'a', not 'A').
+
+        * UIProcess/Automation/ios/WebAutomationSessionIOS.mm:
+        (WebKit::WebAutomationSession::platformSimulateKeyStroke):
+        Use charCodeIgnoringModifiersForVirtualKey().
+
+        * UIProcess/Automation/mac/WebAutomationSessionMac.mm:
+        (WebKit::WebAutomationSession::sendSynthesizedEventsToPage): use -becomeKeyWindow.
+        (WebKit::keyHasStickyModifier): Added.
+        (WebKit::keyCodeForVirtualKey): Added.
+        (WebKit::eventModifierFlagsForVirtualKey):Added.
+        (WebKit::WebAutomationSession::platformSimulateKeyStroke):
+        Separately compute key stickiness, keyCode, event modifier, and charCode for
+        the simulated keystroke. The code to compute charCode is now shared between
+        iOS and macOS since the PUA characters are the same for both AppKit and UIKit.
+
 2017-03-17  Brady Eidson  <beidson@apple.com>
 
         Make HTTPCookieStorage operate in the UIProcess in absence of a WebProcessPool.
index 5661c77..5608788 100644 (file)
@@ -54,6 +54,7 @@ struct Cookie;
 
 #if PLATFORM(COCOA)
 OBJC_CLASS NSArray;
+typedef unsigned short unichar;
 #endif
 
 #if USE(APPKIT)
@@ -175,6 +176,9 @@ private:
 #if PLATFORM(COCOA)
     // The type parameter of the NSArray argument is platform-dependent.
     void sendSynthesizedEventsToPage(WebPageProxy&, NSArray *eventsToSend);
+
+    std::optional<unichar> charCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey) const;
+    std::optional<unichar> charCodeIgnoringModifiersForVirtualKey(Inspector::Protocol::Automation::VirtualKey) const;
 #endif
 
     WebProcessPool* m_processPool { nullptr };
index beadecb..455f7f2 100644 (file)
@@ -31,6 +31,7 @@
 #if PLATFORM(IOS)
 #include <ImageIO/CGImageDestination.h>
 #include <MobileCoreServices/UTCoreTypes.h>
+#include <WebCore/KeyEventCodesIOS.h>
 #endif
 
 using namespace WebCore;
@@ -55,6 +56,134 @@ std::optional<String> WebAutomationSession::platformGetBase64EncodedPNGData(cons
     return String([imageData base64EncodedStringWithOptions:0]);
 }
 
+std::optional<unichar> WebAutomationSession::charCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey key) const
+{
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::Shift:
+    case Inspector::Protocol::Automation::VirtualKey::Control:
+    case Inspector::Protocol::Automation::VirtualKey::Alternate:
+    case Inspector::Protocol::Automation::VirtualKey::Meta:
+    case Inspector::Protocol::Automation::VirtualKey::Command:
+        return std::nullopt;
+    case Inspector::Protocol::Automation::VirtualKey::Help:
+        return NSHelpFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Backspace:
+        return NSBackspaceCharacter;
+    case Inspector::Protocol::Automation::VirtualKey::Tab:
+        return NSTabCharacter;
+    case Inspector::Protocol::Automation::VirtualKey::Clear:
+        return NSClearLineFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Enter:
+        return NSEnterCharacter;
+    case Inspector::Protocol::Automation::VirtualKey::Pause:
+        return NSPauseFunctionKey;
+    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:
+        return 0x1B;
+    case Inspector::Protocol::Automation::VirtualKey::PageUp:
+        return NSPageUpFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::PageDown:
+        return NSPageDownFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::End:
+        return NSEndFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Home:
+        return NSHomeFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
+        return NSLeftArrowFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::UpArrow:
+        return NSUpArrowFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::RightArrow:
+        return NSRightArrowFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::DownArrow:
+        return NSDownArrowFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Insert:
+        return NSInsertFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Delete:
+        return NSDeleteFunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Space:
+        return ' ';
+    case Inspector::Protocol::Automation::VirtualKey::Semicolon:
+        return ';';
+    case Inspector::Protocol::Automation::VirtualKey::Equals:
+        return '=';
+    case Inspector::Protocol::Automation::VirtualKey::Return:
+        return NSCarriageReturnCharacter;
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
+        return '0';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
+        return '1';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
+        return '2';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
+        return '3';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
+        return '4';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
+        return '5';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
+        return '6';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
+        return '7';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
+        return '8';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
+        return '9';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
+        return '*';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
+        return '+';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
+        return '-';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
+        // The 'Separator' key is only present on a few international keyboards.
+        // It is usually mapped to the same character as Decimal ('.' or ',').
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
+        return '.';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
+        return '/';
+    case Inspector::Protocol::Automation::VirtualKey::Function1:
+        return NSF1FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function2:
+        return NSF2FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function3:
+        return NSF3FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function4:
+        return NSF4FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function5:
+        return NSF5FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function6:
+        return NSF6FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function7:
+        return NSF7FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function8:
+        return NSF8FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function9:
+        return NSF9FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function10:
+        return NSF10FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function11:
+        return NSF11FunctionKey;
+    case Inspector::Protocol::Automation::VirtualKey::Function12:
+        return NSF12FunctionKey;
+    default:
+        return std::nullopt;
+    }
+}
+
+std::optional<unichar> WebAutomationSession::charCodeIgnoringModifiersForVirtualKey(Inspector::Protocol::Automation::VirtualKey key) const
+{
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
+        return '8';
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
+        return '=';
+    default:
+        return charCodeForVirtualKey(key);
+    }
+}
+
 } // namespace WebKit
 
 #endif // PLATFORM(COCOA)
index 7f4c2e1..5f74ade 100644 (file)
@@ -70,11 +70,7 @@ void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspect
     // The modifiers changed by the virtual key when it is pressed or released.
     WebEventFlags changedModifiers = 0;
 
-    // UIKit does not send key codes for virtual keys even for a hardware keyboard.
-    // Instead, it sends single unichars and WebCore maps these to "windows" key codes.
-    // Synthesize a single unichar such that the correct key code is inferred.
-    std::optional<unichar> charCode = std::nullopt;
-
+    // Figure out the effects of sticky modifiers.
     switch (key) {
     case Inspector::Protocol::Automation::VirtualKey::Shift:
         changedModifiers |= WebEventFlagMaskShift;
@@ -91,166 +87,21 @@ void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspect
     case Inspector::Protocol::Automation::VirtualKey::Command:
         changedModifiers |= WebEventFlagMaskCommand;
         break;
-    case Inspector::Protocol::Automation::VirtualKey::Help:
-        charCode = NSHelpFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Backspace:
-        charCode = NSBackspaceCharacter;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Tab:
-        charCode = NSTabCharacter;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Clear:
-        charCode = NSClearLineFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Enter:
-        charCode = NSEnterCharacter;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Pause:
-        charCode = NSPauseFunctionKey;
-        break;
-    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:
-        charCode = 0x1B;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::PageUp:
-        charCode = NSPageUpFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::PageDown:
-        charCode = NSPageDownFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::End:
-        charCode = NSEndFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Home:
-        charCode = NSHomeFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
-        charCode = NSLeftArrowFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::UpArrow:
-        charCode = NSUpArrowFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::RightArrow:
-        charCode = NSRightArrowFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::DownArrow:
-        charCode = NSDownArrowFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Insert:
-        charCode = NSInsertFunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Delete:
-        charCode = NSDeleteCharacter;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Space:
-        charCode = ' ';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Semicolon:
-        charCode = ';';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Equals:
-        charCode = '=';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Return:
-        charCode = NSCarriageReturnCharacter;
-        break;
-
-    // On iOS, it seems to be irrelevant in later processing whether the number came from number pad or not.
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
-        charCode = '0';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
-        charCode = '1';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
-        charCode = '2';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
-        charCode = '3';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
-        charCode = '4';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
-        charCode = '5';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
-        charCode = '6';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
-        charCode = '7';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
-        charCode = '8';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
-        charCode = '9';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
-        charCode = '*';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
-        charCode = '+';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
-        charCode = '-';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
-        // The 'Separator' key is only present on a few international keyboards.
-        // It is usually mapped to the same character as Decimal ('.' or ',').
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
-        charCode = '.';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
-        charCode = '/';
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function1:
-        charCode = NSF1FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function2:
-        charCode = NSF2FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function3:
-        charCode = NSF3FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function4:
-        charCode = NSF4FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function5:
-        charCode = NSF5FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function6:
-        charCode = NSF6FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function7:
-        charCode = NSF7FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function8:
-        charCode = NSF8FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function9:
-        charCode = NSF9FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function10:
-        charCode = NSF10FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function11:
-        charCode = NSF11FunctionKey;
-        break;
-    case Inspector::Protocol::Automation::VirtualKey::Function12:
-        charCode = NSF12FunctionKey;
+    default:
         break;
     }
 
-    ASSERT(changedModifiers || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
+    // UIKit does not send key codes for virtual keys even for a hardware keyboard.
+    // Instead, it sends single unichars and WebCore maps these to "windows" key codes.
+    // Synthesize a single unichar such that the correct key code is inferred.
+    std::optional<unichar> charCode = charCodeForVirtualKey(key);
+    std::optional<unichar> charCodeIgnoringModifiers = charCodeIgnoringModifiersForVirtualKey(key);
 
     // FIXME: consider using UIKit SPI to normalize 'characters', i.e., changing * to Shift-8,
     // and passing that in to charactersIgnoringModifiers. This is probably not worth the trouble
     // unless it causes an actual behavioral difference.
     NSString *characters = charCode ? [NSString stringWithCharacters:&charCode.value() length:1] : nil;
+    NSString *unmodifiedCharacters = charCodeIgnoringModifiers ? [NSString stringWithCharacters:&charCodeIgnoringModifiers.value() length:1] : nil;
     BOOL isTabKey = charCode && charCode.value() == NSTabCharacter;
 
     // This is used as WebEvent.keyboardFlags, which are only used if we need to
@@ -267,19 +118,19 @@ void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspect
     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress: {
         m_currentModifiers |= changedModifiers;
 
-        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyDown timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:characters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
+        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyDown timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:unmodifiedCharacters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
         break;
     }
     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
         m_currentModifiers &= ~changedModifiers;
 
-        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyUp timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:characters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
+        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyUp timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:unmodifiedCharacters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
         break;
     }
     case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
         // Modifiers only change with KeyPress or KeyRelease, this code path is for single characters.
-        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyDown timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:characters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
-        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyUp timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:characters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
+        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyDown timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:unmodifiedCharacters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
+        [eventsToBeSent addObject:[[[::WebEvent alloc] initWithKeyEventType:WebEventKeyUp timeStamp:CFAbsoluteTimeGetCurrent() characters:characters charactersIgnoringModifiers:unmodifiedCharacters modifiers:m_currentModifiers isRepeating:NO withFlags:inputFlags withInputManagerHint:nil keyCode:keyCode isTabKey:isTabKey] autorelease]];
         break;
     }
     }
index 7adfb35..1e68f14 100644 (file)
@@ -151,7 +151,7 @@ void WebAutomationSession::sendSynthesizedEventsToPage(WebPageProxy& page, NSArr
     for (NSEvent *event in eventsToSend) {
         // Take focus back in case the Inspector became focused while the prior command or
         // NSEvent was delivered to the window.
-        [window makeKeyAndOrderFront:nil];
+        [window becomeKeyWindow];
 
         markEventAsSynthesizedForAutomation(event);
         [window sendEvent:event];
@@ -283,264 +283,246 @@ void WebAutomationSession::platformSimulateMouseInteraction(WebPageProxy& page,
     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
 }
 
-void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
+static bool keyHasStickyModifier(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 = @"";
+    // Returns whether the key's modifier flags should affect other events while pressed down.
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::Shift:
+    case Inspector::Protocol::Automation::VirtualKey::Control:
+    case Inspector::Protocol::Automation::VirtualKey::Alternate:
+    case Inspector::Protocol::Automation::VirtualKey::Meta:
+    case Inspector::Protocol::Automation::VirtualKey::Command:
+        return true;
 
-    // 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/
+    default:
+        return false;
+    }
+}
 
+static int keyCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey key)
+{
+    // The likely keyCode for the virtual key as defined in <HIToolbox/Events.h>.
     switch (key) {
     case Inspector::Protocol::Automation::VirtualKey::Shift:
-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagShift;
-        keyCode = kVK_Shift;
-        break;
+        return kVK_Shift;
     case Inspector::Protocol::Automation::VirtualKey::Control:
-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagControl;
-        keyCode = kVK_Control;
-        break;
+        return kVK_Control;
     case Inspector::Protocol::Automation::VirtualKey::Alternate:
-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagOption;
-        keyCode = kVK_Option;
-        break;
+        return kVK_Option;
     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;
+        return kVK_Command;
     case Inspector::Protocol::Automation::VirtualKey::Help:
-        changedModifiers |= NSEventModifierFlagHelp | NSEventModifierFlagFunction;
-        keyCode = kVK_Help;
-        break;
+        return kVK_Help;
     case Inspector::Protocol::Automation::VirtualKey::Backspace:
-        keyCode = kVK_Delete;
-        break;
+        return kVK_Delete;
     case Inspector::Protocol::Automation::VirtualKey::Tab:
-        keyCode = kVK_Tab;
-        break;
+        return kVK_Tab;
     case Inspector::Protocol::Automation::VirtualKey::Clear:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadClear;
-        break;
+        return kVK_ANSI_KeypadClear;
     case Inspector::Protocol::Automation::VirtualKey::Enter:
-        keyCode = kVK_ANSI_KeypadEnter;
-        break;
+        return kVK_ANSI_KeypadEnter;
     case Inspector::Protocol::Automation::VirtualKey::Pause:
-        // The 'pause' key does not exist on Apple keyboards and has no keycode.
+        // The 'pause' key does not exist on Apple keyboards and has no keyCode.
         // The semantics are unclear so just abort and do nothing.
-        return;
+        return 0;
     case Inspector::Protocol::Automation::VirtualKey::Cancel:
-        // The 'cancel' key does not exist on Apple keyboards and has no keycode.
+        // 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;
+        return kVK_Escape;
     case Inspector::Protocol::Automation::VirtualKey::PageUp:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_PageUp;
-        break;
+        return kVK_PageUp;
     case Inspector::Protocol::Automation::VirtualKey::PageDown:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_PageDown;
-        break;
+        return kVK_PageDown;
     case Inspector::Protocol::Automation::VirtualKey::End:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_End;
-        break;
+        return kVK_End;
     case Inspector::Protocol::Automation::VirtualKey::Home:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_Home;
-        break;
+        return kVK_Home;
     case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_LeftArrow;
-        break;
+        return kVK_LeftArrow;
     case Inspector::Protocol::Automation::VirtualKey::UpArrow:
-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_UpArrow;
-        break;
+        return kVK_UpArrow;
     case Inspector::Protocol::Automation::VirtualKey::RightArrow:
-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_RightArrow;
-        break;
+        return kVK_RightArrow;
     case Inspector::Protocol::Automation::VirtualKey::DownArrow:
-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_DownArrow;
-        break;
+        return kVK_DownArrow;
     case Inspector::Protocol::Automation::VirtualKey::Insert:
-        // The 'insert' key does not exist on Apple keyboards and has no keycode.
+        // The 'insert' key does not exist on Apple keyboards and has no keyCode.
         // The semantics are unclear so just abort and do nothing.
-        return;
+        return 0;
     case Inspector::Protocol::Automation::VirtualKey::Delete:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_ForwardDelete;
-        break;
+        return kVK_ForwardDelete;
     case Inspector::Protocol::Automation::VirtualKey::Space:
-        keyCode = kVK_Space;
-        characters = @" ";
-        break;
+        return kVK_Space;
     case Inspector::Protocol::Automation::VirtualKey::Semicolon:
-        keyCode = kVK_ANSI_Semicolon;
-        characters = @";";
-        break;
+        return kVK_ANSI_Semicolon;
     case Inspector::Protocol::Automation::VirtualKey::Equals:
-        keyCode = kVK_ANSI_Equal;
-        characters = @"=";
-        break;
+        return kVK_ANSI_Equal;
     case Inspector::Protocol::Automation::VirtualKey::Return:
-        keyCode = kVK_Return;
-        break;
+        return kVK_Return;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad0;
-        characters = @"0";
-        break;
+        return kVK_ANSI_Keypad0;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad1;
-        characters = @"1";
-        break;
+        return kVK_ANSI_Keypad1;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad2;
-        characters = @"2";
-        break;
+        return kVK_ANSI_Keypad2;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad3;
-        characters = @"3";
-        break;
+        return kVK_ANSI_Keypad3;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad4;
-        characters = @"4";
-        break;
+        return kVK_ANSI_Keypad4;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad5;
-        characters = @"5";
-        break;
+        return kVK_ANSI_Keypad5;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad6;
-        characters = @"6";
-        break;
+        return kVK_ANSI_Keypad6;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad7;
-        characters = @"7";
-        break;
+        return kVK_ANSI_Keypad7;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad8;
-        characters = @"8";
-        break;
+        return kVK_ANSI_Keypad8;
     case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad9;
-        characters = @"9";
-        break;
+        return kVK_ANSI_Keypad9;
     case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadMultiply;
-        characters = @"*";
-        break;
+        return kVK_ANSI_KeypadMultiply;
     case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadPlus;
-        characters = @"+";
-        break;
+        return kVK_ANSI_KeypadPlus;
     case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadMinus;
-        characters = @"-";
-        break;
+        return kVK_ANSI_KeypadMinus;
     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;
+        return 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;
+        return kVK_ANSI_KeypadDivide;
     case Inspector::Protocol::Automation::VirtualKey::Function1:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F1;
-        break;
+        return kVK_F1;
     case Inspector::Protocol::Automation::VirtualKey::Function2:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F2;
-        break;
+        return kVK_F2;
     case Inspector::Protocol::Automation::VirtualKey::Function3:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F3;
-        break;
+        return kVK_F3;
     case Inspector::Protocol::Automation::VirtualKey::Function4:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F4;
-        break;
+        return kVK_F4;
     case Inspector::Protocol::Automation::VirtualKey::Function5:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F5;
-        break;
+        return kVK_F5;
     case Inspector::Protocol::Automation::VirtualKey::Function6:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F6;
-        break;
+        return kVK_F6;
     case Inspector::Protocol::Automation::VirtualKey::Function7:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F7;
-        break;
+        return kVK_F7;
     case Inspector::Protocol::Automation::VirtualKey::Function8:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F8;
-        break;
+        return kVK_F8;
     case Inspector::Protocol::Automation::VirtualKey::Function9:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F9;
-        break;
+        return kVK_F9;
     case Inspector::Protocol::Automation::VirtualKey::Function10:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F10;
-        break;
+        return kVK_F10;
     case Inspector::Protocol::Automation::VirtualKey::Function11:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F11;
-        break;
+        return kVK_F11;
     case Inspector::Protocol::Automation::VirtualKey::Function12:
-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F12;
-        break;
+        return kVK_F12;
     }
+}
 
-    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
+static NSEventModifierFlags eventModifierFlagsForVirtualKey(Inspector::Protocol::Automation::VirtualKey key)
+{
+    // Computes 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.
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::Shift:
+        return NSEventModifierFlagShift;
+
+    case Inspector::Protocol::Automation::VirtualKey::Control:
+        return NSEventModifierFlagControl;
+
+    case Inspector::Protocol::Automation::VirtualKey::Alternate:
+        return NSEventModifierFlagOption;
+
+    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:
+        return NSEventModifierFlagCommand;
+
+    case Inspector::Protocol::Automation::VirtualKey::Help:
+        return NSEventModifierFlagHelp | NSEventModifierFlagFunction;
+
+    case Inspector::Protocol::Automation::VirtualKey::PageUp:
+    case Inspector::Protocol::Automation::VirtualKey::PageDown:
+    case Inspector::Protocol::Automation::VirtualKey::End:
+    case Inspector::Protocol::Automation::VirtualKey::Home:
+        return NSEventModifierFlagFunction;
 
-    ASSERT(isStickyModifier || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
+    case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
+    case Inspector::Protocol::Automation::VirtualKey::UpArrow:
+    case Inspector::Protocol::Automation::VirtualKey::RightArrow:
+    case Inspector::Protocol::Automation::VirtualKey::DownArrow:
+        return NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
+
+    case Inspector::Protocol::Automation::VirtualKey::Delete:
+        return NSEventModifierFlagFunction;
+
+    case Inspector::Protocol::Automation::VirtualKey::Clear:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
+    case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
+        return NSEventModifierFlagNumericPad;
+
+    case Inspector::Protocol::Automation::VirtualKey::Function1:
+    case Inspector::Protocol::Automation::VirtualKey::Function2:
+    case Inspector::Protocol::Automation::VirtualKey::Function3:
+    case Inspector::Protocol::Automation::VirtualKey::Function4:
+    case Inspector::Protocol::Automation::VirtualKey::Function5:
+    case Inspector::Protocol::Automation::VirtualKey::Function6:
+    case Inspector::Protocol::Automation::VirtualKey::Function7:
+    case Inspector::Protocol::Automation::VirtualKey::Function8:
+    case Inspector::Protocol::Automation::VirtualKey::Function9:
+    case Inspector::Protocol::Automation::VirtualKey::Function10:
+    case Inspector::Protocol::Automation::VirtualKey::Function11:
+    case Inspector::Protocol::Automation::VirtualKey::Function12:
+        return NSEventModifierFlagFunction;
+
+    default:
+        return 0;
+    }
+}
+
+void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
+{
+    // 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/
+
+    bool isStickyModifier = keyHasStickyModifier(key);
+    NSEventModifierFlags changedModifiers = eventModifierFlagsForVirtualKey(key);
+    int keyCode = keyCodeForVirtualKey(key);
+
+    // FIXME: consider using AppKit SPI to normalize 'characters', i.e., changing * to Shift-8,
+    // and passing that in to charactersIgnoringModifiers. We could hardcode this for ASCII if needed.
+    std::optional<unichar> charCode = charCodeForVirtualKey(key);
+    std::optional<unichar> charCodeIgnoringModifiers = charCodeIgnoringModifiersForVirtualKey(key);
+    NSString *characters = charCode ? [NSString stringWithCharacters:&charCode.value() length:1] : nil;
+    NSString *unmodifiedCharacters = charCodeIgnoringModifiers ? [NSString stringWithCharacters:&charCodeIgnoringModifiers.value() length:1] : nil;
+
+    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
 
     NSEventModifierFlags existingModifiers = [NSEvent modifierFlags];
     NSEventModifierFlags updatedModifiers = 0;
+
+    // FIXME: this timestamp is not even close to matching native events. Find out how to get closer.
     NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
     NSWindow *window = page.platformWindow();
     NSInteger windowNumber = window.windowNumber;
@@ -550,13 +532,13 @@ void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspect
     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]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters 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]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters isARepeat:NO keyCode:keyCode]];
         break;
     }
     case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
@@ -566,8 +548,8 @@ void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspect
             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]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyDown location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters isARepeat:NO keyCode:keyCode]];
+        [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyUp location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters isARepeat:NO keyCode:keyCode]];
         break;
     }
     }