<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[214144] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/214144">214144</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2017-03-19 08:54:32 -0700 (Sun, 19 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Cocoa] Web Automation: non-sticky virtual keys like 'left arrow' don't work properly
https://bugs.webkit.org/show_bug.cgi?id=169733
&lt;rdar://problem/30162608&gt;

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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationWebAutomationSessionh">trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationcocoaWebAutomationSessionCocoamm">trunk/Source/WebKit2/UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationiosWebAutomationSessionIOSmm">trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationmacWebAutomationSessionMacmm">trunk/Source/WebKit2/UIProcess/Automation/mac/WebAutomationSessionMac.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (214143 => 214144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2017-03-19 15:41:36 UTC (rev 214143)
+++ trunk/Source/WebKit2/ChangeLog        2017-03-19 15:54:32 UTC (rev 214144)
</span><span class="lines">@@ -1,3 +1,60 @@
</span><ins>+2017-03-19  Brian Burg  &lt;bburg@apple.com&gt;
+
+        [Cocoa] Web Automation: non-sticky virtual keys like 'left arrow' don't work properly
+        https://bugs.webkit.org/show_bug.cgi?id=169733
+        &lt;rdar://problem/30162608&gt;
+
+        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.
+
</ins><span class="cx"> 2017-03-17  Brady Eidson  &lt;beidson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make HTTPCookieStorage operate in the UIProcess in absence of a WebProcessPool.
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationWebAutomationSessionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h (214143 => 214144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h        2017-03-19 15:41:36 UTC (rev 214143)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h        2017-03-19 15:54:32 UTC (rev 214144)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx"> OBJC_CLASS NSArray;
</span><ins>+typedef unsigned short unichar;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if USE(APPKIT)
</span><span class="lines">@@ -175,6 +176,9 @@
</span><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">     // The type parameter of the NSArray argument is platform-dependent.
</span><span class="cx">     void sendSynthesizedEventsToPage(WebPageProxy&amp;, NSArray *eventsToSend);
</span><ins>+
+    std::optional&lt;unichar&gt; charCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey) const;
+    std::optional&lt;unichar&gt; charCodeIgnoringModifiersForVirtualKey(Inspector::Protocol::Automation::VirtualKey) const;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     WebProcessPool* m_processPool { nullptr };
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationcocoaWebAutomationSessionCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm (214143 => 214144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm        2017-03-19 15:41:36 UTC (rev 214143)
+++ trunk/Source/WebKit2/UIProcess/Automation/cocoa/WebAutomationSessionCocoa.mm        2017-03-19 15:54:32 UTC (rev 214144)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx"> #include &lt;ImageIO/CGImageDestination.h&gt;
</span><span class="cx"> #include &lt;MobileCoreServices/UTCoreTypes.h&gt;
</span><ins>+#include &lt;WebCore/KeyEventCodesIOS.h&gt;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> using namespace WebCore;
</span><span class="lines">@@ -55,6 +56,134 @@
</span><span class="cx">     return String([imageData base64EncodedStringWithOptions:0]);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+std::optional&lt;unichar&gt; 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&lt;unichar&gt; 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);
+    }
+}
+
</ins><span class="cx"> } // namespace WebKit
</span><span class="cx"> 
</span><span class="cx"> #endif // PLATFORM(COCOA)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationiosWebAutomationSessionIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm (214143 => 214144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm        2017-03-19 15:41:36 UTC (rev 214143)
+++ trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm        2017-03-19 15:54:32 UTC (rev 214144)
</span><span class="lines">@@ -70,11 +70,7 @@
</span><span class="cx">     // The modifiers changed by the virtual key when it is pressed or released.
</span><span class="cx">     WebEventFlags changedModifiers = 0;
</span><span class="cx"> 
</span><del>-    // UIKit does not send key codes for virtual keys even for a hardware keyboard.
-    // Instead, it sends single unichars and WebCore maps these to &quot;windows&quot; key codes.
-    // Synthesize a single unichar such that the correct key code is inferred.
-    std::optional&lt;unichar&gt; charCode = std::nullopt;
-
</del><ins>+    // Figure out the effects of sticky modifiers.
</ins><span class="cx">     switch (key) {
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Shift:
</span><span class="cx">         changedModifiers |= WebEventFlagMaskShift;
</span><span class="lines">@@ -91,166 +87,21 @@
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Command:
</span><span class="cx">         changedModifiers |= WebEventFlagMaskCommand;
</span><span class="cx">         break;
</span><del>-    case Inspector::Protocol::Automation::VirtualKey::Help:
-        charCode = NSHelpFunctionKey;
</del><ins>+    default:
</ins><span class="cx">         break;
</span><del>-    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;
-        break;
</del><span class="cx">     }
</span><span class="cx"> 
</span><del>-    ASSERT(changedModifiers || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
</del><ins>+    // UIKit does not send key codes for virtual keys even for a hardware keyboard.
+    // Instead, it sends single unichars and WebCore maps these to &quot;windows&quot; key codes.
+    // Synthesize a single unichar such that the correct key code is inferred.
+    std::optional&lt;unichar&gt; charCode = charCodeForVirtualKey(key);
+    std::optional&lt;unichar&gt; charCodeIgnoringModifiers = charCodeIgnoringModifiersForVirtualKey(key);
</ins><span class="cx"> 
</span><span class="cx">     // FIXME: consider using UIKit SPI to normalize 'characters', i.e., changing * to Shift-8,
</span><span class="cx">     // and passing that in to charactersIgnoringModifiers. This is probably not worth the trouble
</span><span class="cx">     // unless it causes an actual behavioral difference.
</span><span class="cx">     NSString *characters = charCode ? [NSString stringWithCharacters:&amp;charCode.value() length:1] : nil;
</span><ins>+    NSString *unmodifiedCharacters = charCodeIgnoringModifiers ? [NSString stringWithCharacters:&amp;charCodeIgnoringModifiers.value() length:1] : nil;
</ins><span class="cx">     BOOL isTabKey = charCode &amp;&amp; charCode.value() == NSTabCharacter;
</span><span class="cx"> 
</span><span class="cx">     // This is used as WebEvent.keyboardFlags, which are only used if we need to
</span><span class="lines">@@ -267,19 +118,19 @@
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress: {
</span><span class="cx">         m_currentModifiers |= changedModifiers;
</span><span class="cx"> 
</span><del>-        [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]];
</del><ins>+        [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]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
</span><span class="cx">         m_currentModifiers &amp;= ~changedModifiers;
</span><span class="cx"> 
</span><del>-        [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]];
</del><ins>+        [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]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
</span><span class="cx">         // Modifiers only change with KeyPress or KeyRelease, this code path is for single characters.
</span><del>-        [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]];
</del><ins>+        [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]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationmacWebAutomationSessionMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/mac/WebAutomationSessionMac.mm (214143 => 214144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/mac/WebAutomationSessionMac.mm        2017-03-19 15:41:36 UTC (rev 214143)
+++ trunk/Source/WebKit2/UIProcess/Automation/mac/WebAutomationSessionMac.mm        2017-03-19 15:54:32 UTC (rev 214144)
</span><span class="lines">@@ -151,7 +151,7 @@
</span><span class="cx">     for (NSEvent *event in eventsToSend) {
</span><span class="cx">         // Take focus back in case the Inspector became focused while the prior command or
</span><span class="cx">         // NSEvent was delivered to the window.
</span><del>-        [window makeKeyAndOrderFront:nil];
</del><ins>+        [window becomeKeyWindow];
</ins><span class="cx"> 
</span><span class="cx">         markEventAsSynthesizedForAutomation(event);
</span><span class="cx">         [window sendEvent:event];
</span><span class="lines">@@ -283,264 +283,246 @@
</span><span class="cx">     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy&amp; page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
</del><ins>+static bool keyHasStickyModifier(Inspector::Protocol::Automation::VirtualKey key)
</ins><span class="cx"> {
</span><del>-    // 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 &lt;HIToolbox/Events.h&gt;.
-    int keyCode = 0;
-    // Typical characters produced by the virtual key, if any.
-    NSString *characters = @&quot;&quot;;
</del><ins>+    // 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;
</ins><span class="cx"> 
</span><del>-    // 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/
</del><ins>+    default:
+        return false;
+    }
+}
</ins><span class="cx"> 
</span><ins>+static int keyCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey key)
+{
+    // The likely keyCode for the virtual key as defined in &lt;HIToolbox/Events.h&gt;.
</ins><span class="cx">     switch (key) {
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Shift:
</span><del>-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagShift;
-        keyCode = kVK_Shift;
-        break;
</del><ins>+        return kVK_Shift;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Control:
</span><del>-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagControl;
-        keyCode = kVK_Control;
-        break;
</del><ins>+        return kVK_Control;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Alternate:
</span><del>-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagOption;
-        keyCode = kVK_Option;
-        break;
</del><ins>+        return kVK_Option;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Meta:
</span><span class="cx">         // The 'meta' key does not exist on Apple keyboards and is usually
</span><span class="cx">         // mapped to the Command key when using third-party keyboards.
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Command:
</span><del>-        isStickyModifier = true;
-        changedModifiers |= NSEventModifierFlagCommand;
-        keyCode = kVK_Command;
-        break;
</del><ins>+        return kVK_Command;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Help:
</span><del>-        changedModifiers |= NSEventModifierFlagHelp | NSEventModifierFlagFunction;
-        keyCode = kVK_Help;
-        break;
</del><ins>+        return kVK_Help;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Backspace:
</span><del>-        keyCode = kVK_Delete;
-        break;
</del><ins>+        return kVK_Delete;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Tab:
</span><del>-        keyCode = kVK_Tab;
-        break;
</del><ins>+        return kVK_Tab;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Clear:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadClear;
-        break;
</del><ins>+        return kVK_ANSI_KeypadClear;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Enter:
</span><del>-        keyCode = kVK_ANSI_KeypadEnter;
-        break;
</del><ins>+        return kVK_ANSI_KeypadEnter;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Pause:
</span><del>-        // The 'pause' key does not exist on Apple keyboards and has no keycode.
</del><ins>+        // The 'pause' key does not exist on Apple keyboards and has no keyCode.
</ins><span class="cx">         // The semantics are unclear so just abort and do nothing.
</span><del>-        return;
</del><ins>+        return 0;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Cancel:
</span><del>-        // The 'cancel' key does not exist on Apple keyboards and has no keycode.
</del><ins>+        // The 'cancel' key does not exist on Apple keyboards and has no keyCode.
</ins><span class="cx">         // According to the internet its functionality is similar to 'Escape'.
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Escape:
</span><del>-        keyCode = kVK_Escape;
-        break;
</del><ins>+        return kVK_Escape;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::PageUp:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_PageUp;
-        break;
</del><ins>+        return kVK_PageUp;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::PageDown:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_PageDown;
-        break;
</del><ins>+        return kVK_PageDown;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::End:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_End;
-        break;
</del><ins>+        return kVK_End;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Home:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_Home;
-        break;
</del><ins>+        return kVK_Home;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_LeftArrow;
-        break;
</del><ins>+        return kVK_LeftArrow;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::UpArrow:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_UpArrow;
-        break;
</del><ins>+        return kVK_UpArrow;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::RightArrow:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_RightArrow;
-        break;
</del><ins>+        return kVK_RightArrow;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::DownArrow:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
-        keyCode = kVK_DownArrow;
-        break;
</del><ins>+        return kVK_DownArrow;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Insert:
</span><del>-        // The 'insert' key does not exist on Apple keyboards and has no keycode.
</del><ins>+        // The 'insert' key does not exist on Apple keyboards and has no keyCode.
</ins><span class="cx">         // The semantics are unclear so just abort and do nothing.
</span><del>-        return;
</del><ins>+        return 0;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Delete:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_ForwardDelete;
-        break;
</del><ins>+        return kVK_ForwardDelete;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Space:
</span><del>-        keyCode = kVK_Space;
-        characters = @&quot; &quot;;
-        break;
</del><ins>+        return kVK_Space;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Semicolon:
</span><del>-        keyCode = kVK_ANSI_Semicolon;
-        characters = @&quot;;&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Semicolon;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Equals:
</span><del>-        keyCode = kVK_ANSI_Equal;
-        characters = @&quot;=&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Equal;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Return:
</span><del>-        keyCode = kVK_Return;
-        break;
</del><ins>+        return kVK_Return;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad0;
-        characters = @&quot;0&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad0;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad1;
-        characters = @&quot;1&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad1;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad2;
-        characters = @&quot;2&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad2;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad3;
-        characters = @&quot;3&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad3;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad4;
-        characters = @&quot;4&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad4;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad5;
-        characters = @&quot;5&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad5;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad6;
-        characters = @&quot;6&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad6;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad7;
-        characters = @&quot;7&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad7;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad8;
-        characters = @&quot;8&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad8;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_Keypad9;
-        characters = @&quot;9&quot;;
-        break;
</del><ins>+        return kVK_ANSI_Keypad9;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadMultiply;
-        characters = @&quot;*&quot;;
-        break;
</del><ins>+        return kVK_ANSI_KeypadMultiply;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadPlus;
-        characters = @&quot;+&quot;;
-        break;
</del><ins>+        return kVK_ANSI_KeypadPlus;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadMinus;
-        characters = @&quot;-&quot;;
-        break;
</del><ins>+        return kVK_ANSI_KeypadMinus;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
</del><span class="cx">         // The 'Separator' key is only present on a few international keyboards.
</span><span class="cx">         // It is usually mapped to the same character as Decimal ('.' or ',').
</span><span class="cx">         FALLTHROUGH;
</span><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadDecimal;
</del><ins>+        return kVK_ANSI_KeypadDecimal;
</ins><span class="cx">         // FIXME: this might be locale-dependent. See the above comment.
</span><del>-        characters = @&quot;.&quot;;
-        break;
</del><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
</span><del>-        changedModifiers |= NSEventModifierFlagNumericPad;
-        keyCode = kVK_ANSI_KeypadDivide;
-        characters = @&quot;/&quot;;
-        break;
</del><ins>+        return kVK_ANSI_KeypadDivide;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function1:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F1;
-        break;
</del><ins>+        return kVK_F1;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function2:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F2;
-        break;
</del><ins>+        return kVK_F2;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function3:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F3;
-        break;
</del><ins>+        return kVK_F3;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function4:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F4;
-        break;
</del><ins>+        return kVK_F4;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function5:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F5;
-        break;
</del><ins>+        return kVK_F5;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function6:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F6;
-        break;
</del><ins>+        return kVK_F6;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function7:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F7;
-        break;
</del><ins>+        return kVK_F7;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function8:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F8;
-        break;
</del><ins>+        return kVK_F8;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function9:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F9;
-        break;
</del><ins>+        return kVK_F9;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function10:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F10;
-        break;
</del><ins>+        return kVK_F10;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function11:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F11;
-        break;
</del><ins>+        return kVK_F11;
</ins><span class="cx">     case Inspector::Protocol::Automation::VirtualKey::Function12:
</span><del>-        changedModifiers |= NSEventModifierFlagFunction;
-        keyCode = kVK_F12;
-        break;
</del><ins>+        return kVK_F12;
</ins><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><ins>+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;
+
+    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&amp; 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&lt;unichar&gt; charCode = charCodeForVirtualKey(key);
+    std::optional&lt;unichar&gt; charCodeIgnoringModifiers = charCodeIgnoringModifiersForVirtualKey(key);
+    NSString *characters = charCode ? [NSString stringWithCharacters:&amp;charCode.value() length:1] : nil;
+    NSString *unmodifiedCharacters = charCodeIgnoringModifiers ? [NSString stringWithCharacters:&amp;charCodeIgnoringModifiers.value() length:1] : nil;
+
</ins><span class="cx">     auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
</span><span class="cx"> 
</span><del>-    ASSERT(isStickyModifier || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
-
</del><span class="cx">     NSEventModifierFlags existingModifiers = [NSEvent modifierFlags];
</span><span class="cx">     NSEventModifierFlags updatedModifiers = 0;
</span><ins>+
+    // FIXME: this timestamp is not even close to matching native events. Find out how to get closer.
</ins><span class="cx">     NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
</span><span class="cx">     NSWindow *window = page.platformWindow();
</span><span class="cx">     NSInteger windowNumber = window.windowNumber;
</span><span class="lines">@@ -550,13 +532,13 @@
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress: {
</span><span class="cx">         NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyDown;
</span><span class="cx">         updatedModifiers = existingModifiers | changedModifiers;
</span><del>-        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
</del><ins>+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters isARepeat:NO keyCode:keyCode]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
</span><span class="cx">         NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyUp;
</span><span class="cx">         updatedModifiers = existingModifiers &amp; ~changedModifiers;
</span><del>-        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
</del><ins>+        [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:unmodifiedCharacters isARepeat:NO keyCode:keyCode]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
</span><span class="lines">@@ -566,8 +548,8 @@
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         updatedModifiers = existingModifiers | changedModifiers;
</span><del>-        [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]];
</del><ins>+        [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]];
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     }
</span></span></pre>
</div>
</div>

</body>
</html>