<!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>[214070] 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/214070">214070</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2017-03-16 14:49:30 -0700 (Thu, 16 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS WK2] Web Automation: implement platform methods for simulating keyboard events
https://bugs.webkit.org/show_bug.cgi?id=169487
&lt;rdar://problem/28360564&gt;

Reviewed by Joseph Pecoraro.

Reimplement platformSimulateKey{Stroke, Sequence} to simulate events coming from UIKit.
These events are sent directly into WebCore without going through the keyboard system,
since we don't need to show the keyboard when simulating keystrokes. For example, the
keystrokes may not be representable given the current keyboard layout and system language.

* UIProcess/Automation/WebAutomationSession.cpp:
* UIProcess/Automation/WebAutomationSession.h:
Un-stub implementations that now exist for iOS.

* UIProcess/Automation/ios/WebAutomationSessionIOS.mm: Added.

(WebKit::WebAutomationSession::sendSynthesizedEventsToPage): Added.
This is similar to the Mac version, but it sends events directly into WebPageProxy
instead of sending them to the NSWindow containing the WebView.

(WebKit::WebAutomationSession::platformSimulateKeyStroke):
Use a somewhat different strategy for determining the charCode/keyCode since we cannot
rely on the system keyboard / input manager to generate and deliver real key events.
Compared to the Mac platform method, this requires setting up fewer fields and does
not use AppKit's current modifier state to determine the effect of sticky modifiers.

(WebKit::WebAutomationSession::platformSimulateKeySequence):
This is almost verbatim from the Mac platform method but uses a different event class.

* WebKit2.xcodeproj/project.pbxproj: Add new file.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationWebAutomationSessioncpp">trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationWebAutomationSessionh">trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h</a></li>
<li><a href="#trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj">trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/Source/WebKit2/UIProcess/Automation/ios/</li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationiosWebAutomationSessionIOSmm">trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (214069 => 214070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2017-03-16 21:19:23 UTC (rev 214069)
+++ trunk/Source/WebKit2/ChangeLog        2017-03-16 21:49:30 UTC (rev 214070)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2017-03-16  Brian Burg  &lt;bburg@apple.com&gt;
+
+        [iOS WK2] Web Automation: implement platform methods for simulating keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=169487
+        &lt;rdar://problem/28360564&gt;
+
+        Reviewed by Joseph Pecoraro.
+
+        Reimplement platformSimulateKey{Stroke, Sequence} to simulate events coming from UIKit.
+        These events are sent directly into WebCore without going through the keyboard system,
+        since we don't need to show the keyboard when simulating keystrokes. For example, the
+        keystrokes may not be representable given the current keyboard layout and system language.
+
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        * UIProcess/Automation/WebAutomationSession.h:
+        Un-stub implementations that now exist for iOS.
+
+        * UIProcess/Automation/ios/WebAutomationSessionIOS.mm: Added.
+
+        (WebKit::WebAutomationSession::sendSynthesizedEventsToPage): Added.
+        This is similar to the Mac version, but it sends events directly into WebPageProxy
+        instead of sending them to the NSWindow containing the WebView.
+
+        (WebKit::WebAutomationSession::platformSimulateKeyStroke):
+        Use a somewhat different strategy for determining the charCode/keyCode since we cannot
+        rely on the system keyboard / input manager to generate and deliver real key events.
+        Compared to the Mac platform method, this requires setting up fewer fields and does
+        not use AppKit's current modifier state to determine the effect of sticky modifiers.
+
+        (WebKit::WebAutomationSession::platformSimulateKeySequence):
+        This is almost verbatim from the Mac platform method but uses a different event class.
+
+        * WebKit2.xcodeproj/project.pbxproj: Add new file.
+
</ins><span class="cx"> 2017-03-16  Youenn Fablet  &lt;youenn@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [mac-wk2 debug] LayoutTest webrtc/video-mute.html is timing out
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationWebAutomationSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp (214069 => 214070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp        2017-03-16 21:19:23 UTC (rev 214069)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp        2017-03-16 21:49:30 UTC (rev 214070)
</span><span class="lines">@@ -827,7 +827,7 @@
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::performKeyboardInteractions(ErrorString&amp; errorString, const String&amp; handle, const Inspector::InspectorArray&amp; interactions, Ref&lt;PerformKeyboardInteractionsCallback&gt;&amp;&amp; callback)
</span><span class="cx"> {
</span><del>-#if !USE(APPKIT)
</del><ins>+#if !PLATFORM(COCOA)
</ins><span class="cx">     FAIL_WITH_PREDEFINED_ERROR(NotImplemented);
</span><span class="cx"> #else
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="lines">@@ -897,7 +897,7 @@
</span><span class="cx"> 
</span><span class="cx">     for (auto&amp; action : actionsToPerform)
</span><span class="cx">         action();
</span><del>-#endif // USE(APPKIT)
</del><ins>+#endif // PLATFORM(COCOA)
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::takeScreenshot(ErrorString&amp; errorString, const String&amp; handle, Ref&lt;TakeScreenshotCallback&gt;&amp;&amp; callback)
</span><span class="lines">@@ -938,7 +938,9 @@
</span><span class="cx"> void WebAutomationSession::platformSimulateMouseInteraction(WebKit::WebPageProxy&amp;, const WebCore::IntPoint&amp;, Inspector::Protocol::Automation::MouseInteraction, Inspector::Protocol::Automation::MouseButton, WebEvent::Modifiers)
</span><span class="cx"> {
</span><span class="cx"> }
</span><ins>+#endif // !PLATFORM(MAC)
</ins><span class="cx"> 
</span><ins>+#if !PLATFORM(COCOA)
</ins><span class="cx"> void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy&amp;, Inspector::Protocol::Automation::KeyboardInteractionType, Inspector::Protocol::Automation::VirtualKey)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="lines">@@ -946,9 +948,7 @@
</span><span class="cx"> void WebAutomationSession::platformSimulateKeySequence(WebPageProxy&amp;, const String&amp;)
</span><span class="cx"> {
</span><span class="cx"> }
</span><del>-#endif // !PLATFORM(MAC)
</del><span class="cx"> 
</span><del>-#if !PLATFORM(COCOA)
</del><span class="cx"> std::optional&lt;String&gt; WebAutomationSession::platformGetBase64EncodedPNGData(const ShareableBitmap::Handle&amp;)
</span><span class="cx"> {
</span><span class="cx">     return String();
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationWebAutomationSessionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h (214069 => 214070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h        2017-03-16 21:19:23 UTC (rev 214069)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.h        2017-03-16 21:49:30 UTC (rev 214070)
</span><span class="lines">@@ -52,8 +52,11 @@
</span><span class="cx"> struct Cookie;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if PLATFORM(COCOA)
+OBJC_CLASS NSArray;
+#endif
+
</ins><span class="cx"> #if USE(APPKIT)
</span><del>-OBJC_CLASS NSArray;
</del><span class="cx"> OBJC_CLASS NSEvent;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="lines">@@ -169,7 +172,8 @@
</span><span class="cx">     // Get base64 encoded PNG data from a bitmap.
</span><span class="cx">     std::optional&lt;String&gt; platformGetBase64EncodedPNGData(const ShareableBitmap::Handle&amp;);
</span><span class="cx"> 
</span><del>-#if PLATFORM(MAC)
</del><ins>+#if PLATFORM(COCOA)
+    // The type parameter of the NSArray argument is platform-dependent.
</ins><span class="cx">     void sendSynthesizedEventsToPage(WebPageProxy&amp;, NSArray *eventsToSend);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="lines">@@ -216,6 +220,12 @@
</span><span class="cx"> #if ENABLE(REMOTE_INSPECTOR)
</span><span class="cx">     Inspector::FrontendChannel* m_remoteChannel { nullptr };
</span><span class="cx"> #endif
</span><ins>+
+#if PLATFORM(IOS)
+    // Keep track of currently active modifiers across multiple keystrokes.
+    // We don't synthesize platform keyboard events on iOS, so we need to track it ourselves.
+    unsigned m_currentModifiers { 0 };
+#endif
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationiosWebAutomationSessionIOSmm"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm (0 => 214070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm                                (rev 0)
+++ trunk/Source/WebKit2/UIProcess/Automation/ios/WebAutomationSessionIOS.mm        2017-03-16 21:49:30 UTC (rev 214070)
</span><span class="lines">@@ -0,0 +1,312 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import &quot;config.h&quot;
+#import &quot;WebAutomationSession.h&quot;
+
+#if PLATFORM(IOS)
+
+#import &quot;NativeWebKeyboardEvent.h&quot;
+#import &quot;WebAutomationSessionMacros.h&quot;
+#import &quot;WebPageProxy.h&quot;
+#import &lt;WebCore/KeyEventCodesIOS.h&gt;
+#import &lt;WebCore/NotImplemented.h&gt;
+#import &lt;WebCore/WebEvent.h&gt;
+
+using namespace WebCore;
+
+namespace WebKit {
+
+void WebAutomationSession::sendSynthesizedEventsToPage(WebPageProxy&amp; page, NSArray *eventsToSend)
+{
+    // 'eventsToSend' contains WebCore::WebEvent instances. Use a wrapper type specific to the event type.
+    for (::WebEvent *event in eventsToSend) {
+        switch (event.type) {
+        case WebEventMouseDown:
+        case WebEventMouseUp:
+        case WebEventMouseMoved:
+        case WebEventScrollWheel:
+        case WebEventTouchBegin:
+        case WebEventTouchChange:
+        case WebEventTouchEnd:
+        case WebEventTouchCancel:
+            notImplemented();
+            break;
+
+        case WebEventKeyDown:
+        case WebEventKeyUp:
+            page.handleKeyboardEvent(NativeWebKeyboardEvent(event));
+            break;
+        }
+    }
+}
+
+#pragma mark Commands for Platform: 'iOS'
+
+void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy&amp; page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
+{
+    // 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 &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;
+
+    switch (key) {
+    case Inspector::Protocol::Automation::VirtualKey::Shift:
+        changedModifiers |= WebEventFlagMaskShift;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Control:
+        changedModifiers |= WebEventFlagMaskControl;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Alternate:
+        changedModifiers |= WebEventFlagMaskAlternate;
+        break;
+    case Inspector::Protocol::Automation::VirtualKey::Meta:
+        // The 'meta' key does not exist on Apple keyboards and is usually
+        // mapped to the Command key when using third-party keyboards.
+    case Inspector::Protocol::Automation::VirtualKey::Command:
+        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;
+        break;
+    }
+
+    ASSERT(changedModifiers || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
+
+    // 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:&amp;charCode.value() length:1] : nil;
+    BOOL isTabKey = charCode &amp;&amp; charCode.value() == NSTabCharacter;
+
+    // This is used as WebEvent.keyboardFlags, which are only used if we need to
+    // send this event back to UIKit to be interpreted by the keyboard / input manager.
+    // Just ignore this for now; we can fix it if there's an actual behavioral difference.
+    NSUInteger inputFlags = 0;
+
+    // Provide an empty keyCode so that WebCore infers it from the charCode.
+    uint16_t keyCode = 0;
+
+    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
+
+    switch (interaction) {
+    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]];
+        break;
+    }
+    case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
+        m_currentModifiers &amp;= ~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]];
+        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]];
+        break;
+    }
+    }
+
+    sendSynthesizedEventsToPage(page, eventsToBeSent.get());
+}
+
+void WebAutomationSession::platformSimulateKeySequence(WebPageProxy&amp; page, const String&amp; keySequence)
+{
+    auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
+
+    // Split the text into combining character sequences and send each separately.
+    // This has no similarity to how keyboards work when inputting complex text.
+    // This command is more similar to the 'insertText:' editing command, except
+    // that this emits keyup/keydown/keypress events for roughly each character.
+    // This API should move more towards that direction in the future.
+    NSString *text = keySequence;
+    BOOL isTabKey = [text isEqualToString:@&quot;\t&quot;];
+
+    [text enumerateSubstringsInRange:NSMakeRange(0, text.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
+        [eventsToBeSent addObject:[[::WebEvent alloc] initWithKeyEventType:WebEventKeyDown timeStamp:CFAbsoluteTimeGetCurrent() characters:substring charactersIgnoringModifiers:substring modifiers:m_currentModifiers isRepeating:NO withFlags:0 withInputManagerHint:nil keyCode:0 isTabKey:isTabKey]];
+        [eventsToBeSent addObject:[[::WebEvent alloc] initWithKeyEventType:WebEventKeyUp timeStamp:CFAbsoluteTimeGetCurrent() characters:substring charactersIgnoringModifiers:substring modifiers:m_currentModifiers isRepeating:NO withFlags:0 withInputManagerHint:nil keyCode:0 isTabKey:isTabKey]];
+    }];
+
+    sendSynthesizedEventsToPage(page, eventsToBeSent.get());
+}
+
+} // namespace WebKit
+
+#endif // PLATFORM(IOS)
</ins></span></pre></div>
<a id="trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj (214069 => 214070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2017-03-16 21:19:23 UTC (rev 214069)
+++ trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2017-03-16 21:49:30 UTC (rev 214070)
</span><span class="lines">@@ -1393,6 +1393,7 @@
</span><span class="cx">                 990D28BB1C6539D300986977 /* AutomationSessionClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 990D28B71C6539A000986977 /* AutomationSessionClient.h */; };
</span><span class="cx">                 990D28BC1C6539DA00986977 /* AutomationSessionClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = 990D28B81C6539A000986977 /* AutomationSessionClient.mm */; };
</span><span class="cx">                 990D28C01C6553F100986977 /* APIAutomationSessionClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 990D28B31C6526D400986977 /* APIAutomationSessionClient.h */; };
</span><ins>+                9946EF861E7B027000541E79 /* WebAutomationSessionIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9946EF851E7B026600541E79 /* WebAutomationSessionIOS.mm */; };
</ins><span class="cx">                 9955A6EC1C7980C200EB6A93 /* WebAutomationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 9955A6EB1C7980BB00EB6A93 /* WebAutomationSession.h */; };
</span><span class="cx">                 9955A6ED1C7980CA00EB6A93 /* WebAutomationSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9955A6EA1C7980BB00EB6A93 /* WebAutomationSession.cpp */; };
</span><span class="cx">                 9955A6EF1C79810800EB6A93 /* Automation.json in Headers */ = {isa = PBXBuildFile; fileRef = 9955A6E91C7980BB00EB6A93 /* Automation.json */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -3655,6 +3656,7 @@
</span><span class="cx">                 990D28B31C6526D400986977 /* APIAutomationSessionClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIAutomationSessionClient.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 990D28B71C6539A000986977 /* AutomationSessionClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomationSessionClient.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 990D28B81C6539A000986977 /* AutomationSessionClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutomationSessionClient.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                9946EF851E7B026600541E79 /* WebAutomationSessionIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebAutomationSessionIOS.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 9955A6E91C7980BB00EB6A93 /* Automation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Automation.json; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 9955A6EA1C7980BB00EB6A93 /* WebAutomationSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebAutomationSession.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 9955A6EB1C7980BB00EB6A93 /* WebAutomationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebAutomationSession.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -6346,10 +6348,19 @@
</span><span class="cx">                         path = mac;
</span><span class="cx">                         sourceTree = &quot;&lt;group&gt;&quot;;
</span><span class="cx">                 };
</span><ins>+                9946EF821E7B023D00541E79 /* ios */ = {
+                        isa = PBXGroup;
+                        children = (
+                                9946EF851E7B026600541E79 /* WebAutomationSessionIOS.mm */,
+                        );
+                        path = ios;
+                        sourceTree = &quot;&lt;group&gt;&quot;;
+                };
</ins><span class="cx">                 9955A6E81C79809000EB6A93 /* Automation */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><span class="cx">                                 99C3AE251DAD946700AF5C16 /* cocoa */,
</span><ins>+                                9946EF821E7B023D00541E79 /* ios */,
</ins><span class="cx">                                 99C3AE221DAD8E1400AF5C16 /* mac */,
</span><span class="cx">                                 9955A6E91C7980BB00EB6A93 /* Automation.json */,
</span><span class="cx">                                 9955A6EA1C7980BB00EB6A93 /* WebAutomationSession.cpp */,
</span><span class="lines">@@ -9661,6 +9672,7 @@
</span><span class="cx">                                 7CD3A4821A5D02FA009623B8 /* APINavigation.cpp in Sources */,
</span><span class="cx">                                 BCF69FA31176D01400471A52 /* APINavigationData.cpp in Sources */,
</span><span class="cx">                                 CD491B171E73525500009066 /* UserMediaCaptureManagerProxyMessageReceiver.cpp in Sources */,
</span><ins>+                                9946EF861E7B027000541E79 /* WebAutomationSessionIOS.mm in Sources */,
</ins><span class="cx">                                 B63403F914910D57001070B5 /* APIObject.cpp in Sources */,
</span><span class="cx">                                 378E1A3D181ED6FF0031007A /* APIObject.mm in Sources */,
</span><span class="cx">                                 BC857FB612B830E600EDEB2E /* APIOpenPanelParameters.cpp in Sources */,
</span></span></pre>
</div>
</div>

</body>
</html>