<!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>[166212] trunk</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/166212">166212</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2014-03-24 17:52:46 -0700 (Mon, 24 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Replay: capture and replay keyboard events
https://bugs.webkit.org/show_bug.cgi?id=130314

Reviewed by Joseph Pecoraro.

.:

* ManualTests/inspector/replay-keyboard-events.html: Added.

Source/WebCore:

Upstream support for capturing and replaying non-IME keyboard input.
This works similarly to mouse events. It introduces optional fields and
macros to encode/decode them.

Test: ManualTests/inspector/replay-keyboard-events.html

* replay/ReplayInputDispatchMethods.cpp:
(WebCore::HandleKeyPress::dispatch):
* replay/SerializationMethods.cpp:
(JSC::EncodingTraits&lt;NondeterministicInputBase&gt;::encodeValue):
(JSC::EncodingTraits&lt;NondeterministicInputBase&gt;::decodeValue): Switch existing
encode/decode calls to use the shortening macros.
(JSC::EncodingTraits&lt;KeypressCommand&gt;::encodeValue): Added.
(JSC::EncodingTraits&lt;KeypressCommand&gt;::decodeValue): Added.
(JSC::PlatformKeyboardEventAppKit::PlatformKeyboardEventAppKit): Subclass
PlatformKeyboardEvent so that we can set AppKit-specific members not
initialized through the main constructors.
(JSC::EncodingTraits&lt;PlatformKeyboardEvent&gt;::encodeValue): Added.
(JSC::EncodingTraits&lt;PlatformKeyboardEvent&gt;::decodeValue): Added.
* replay/SerializationMethods.h:
* replay/UserInputBridge.cpp:
(WebCore::UserInputBridge::UserInputBridge): initialize m_state inside a guard.
(WebCore::UserInputBridge::handleKeyEvent): fill in the implementation.
* replay/WebInputs.json:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkChangeLog">trunk/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorereplayReplayInputDispatchMethodscpp">trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp</a></li>
<li><a href="#trunkSourceWebCorereplaySerializationMethodscpp">trunk/Source/WebCore/replay/SerializationMethods.cpp</a></li>
<li><a href="#trunkSourceWebCorereplaySerializationMethodsh">trunk/Source/WebCore/replay/SerializationMethods.h</a></li>
<li><a href="#trunkSourceWebCorereplayUserInputBridgecpp">trunk/Source/WebCore/replay/UserInputBridge.cpp</a></li>
<li><a href="#trunkSourceWebCorereplayWebInputsjson">trunk/Source/WebCore/replay/WebInputs.json</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkManualTestsinspectorreplaykeyboardeventshtml">trunk/ManualTests/inspector/replay-keyboard-events.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/ChangeLog (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/ChangeLog        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/ChangeLog        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2014-03-24  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Replay: capture and replay keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=130314
+
+        Reviewed by Joseph Pecoraro.
+
+        * ManualTests/inspector/replay-keyboard-events.html: Added.
+
</ins><span class="cx"> 2014-03-24  Sangyong Park  &lt;sy302.park@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [EFL] Inspector page is not loaded.
</span></span></pre></div>
<a id="trunkManualTestsinspectorreplaykeyboardeventshtml"></a>
<div class="addfile"><h4>Added: trunk/ManualTests/inspector/replay-keyboard-events.html (0 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/ManualTests/inspector/replay-keyboard-events.html                                (rev 0)
+++ trunk/ManualTests/inspector/replay-keyboard-events.html        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -0,0 +1,104 @@
</span><ins>+&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;
+        &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
+&lt;html lang=&quot;en&quot;&gt;
+&lt;head&gt;
+&lt;script src=&quot;./resources/crypto-md5.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; charset=&quot;utf-8&quot;&gt;
+    document.onkeypress = handleEvent;
+    document.onkeydown = handleEvent;
+    document.onkeyup = handleEvent;
+    window.dumpedEvents = [];
+
+    function handleEvent(event) {
+        var properties = [&quot;type&quot;, &quot;eventPhase&quot;, &quot;bubbles&quot;, &quot;cancelable&quot;, &quot;keyIdentifier&quot;, &quot;location&quot;, &quot;shiftKey&quot;, &quot;altKey&quot;, &quot;metaKey&quot;, &quot;altGraphKey&quot;, &quot;keyCode&quot;, &quot;charCode&quot;];
+        obj = {};
+        for (var key of properties)
+            obj[key] = event[key];
+
+        dumpedEvents.push(obj);
+
+        var block = createBlock(hex_md5(JSON.stringify(obj)), event);
+        var blocksContainer = document.getElementById(&quot;blocks&quot;);
+        blocksContainer.appendChild(block);
+
+        var hashLabel = document.getElementById(&quot;hash&quot;);
+        hash.textContent = hex_md5(JSON.stringify(dumpedEvents));
+    }
+
+    function createBlock(hash, event) {
+
+        function glyphForType(type) {
+            switch (type) {
+            case &quot;keydown&quot;: return &quot;D&quot;;
+            case &quot;keyup&quot;: return &quot;U&quot;;
+            case &quot;keypress&quot;: return &quot;K&quot;;
+            }
+        }
+
+        var color = &quot;#&quot; + hash.substr(0,6);
+        var block = document.createElement(&quot;span&quot;);
+        var eventTypeLabel = document.createElement(&quot;span&quot;);
+        eventTypeLabel.className = &quot;event-label&quot;;
+        eventTypeLabel.textContent = glyphForType(event.type);
+        block.appendChild(eventTypeLabel);
+
+        var keyLabel = document.createElement(&quot;span&quot;);
+        keyLabel.className = &quot;key-label&quot;;
+        var codepointRegEx = /^U\+\d{4}$/;
+        keyLabel.textContent = event.keyIdentifier.match(codepointRegEx) ? String.fromCharCode(event.keyCode) : event.keyIdentifier;
+        block.appendChild(keyLabel);
+        block.style.backgroundColor = color;
+        return block;
+    }
+
+    function stateHash() {
+        return hex_md5(JSON.stringify(dumpedEvents));
+    }
+&lt;/script&gt;
+
+&lt;style type=&quot;text/css&quot;&gt;
+body {
+    max-width: 800px;
+}
+#blocks {
+    display: -webkit-flex;
+    width: 600px;
+    -webkit-flex-flow: row wrap;
+}
+
+#blocks &gt; span {
+    padding: 2px 2px;
+    border-radius: 4px;
+    margin: 2px;
+    font-size: 12px;
+    font-weight: bold;
+    font-family: sans-serif;
+    color: #fff;
+    text-align: center;
+}
+
+.event-label::before {
+    content: '(';
+}
+
+.event-label::after {
+    content: ') ';
+}
+
+.key-label {
+}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p&gt;This page is a manual test for capture and replay of keyboard-related events.&lt;/p&gt;
+&lt;p&gt;Below, a block is created for each keyboard event, where the color is derived from a hash of the keyboard event. At the bottom is a cumulative hash of all keyboard event data.&lt;/p&gt;
+&lt;hr/&gt;
+&lt;p&gt;
+To test the replay functionality, open the Web Inspector, start capturing, and then type text into the textarea. After some time, stop capturing and then replay. The replayed execution should produce the same sequence of blocks. More importantly, the cumulative hash value should be the same at the end of capturing and at the end of any subsequent replays.&lt;/p&gt;
+&lt;/p&gt;
+&lt;hr/&gt;
+&lt;form&gt;&lt;textarea cols=&quot;80&quot; rows=&quot;20&quot;&gt;&lt;/textarea&gt;&lt;/form&gt;
+&lt;div id=&quot;hash&quot;&gt;&lt;/div&gt;
+&lt;div id=&quot;blocks&quot;&gt;&lt;/div&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/ChangeLog        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2014-03-24  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Replay: capture and replay keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=130314
+
+        Reviewed by Joseph Pecoraro.
+
+        Upstream support for capturing and replaying non-IME keyboard input.
+        This works similarly to mouse events. It introduces optional fields and
+        macros to encode/decode them.
+
+        Test: ManualTests/inspector/replay-keyboard-events.html
+
+        * replay/ReplayInputDispatchMethods.cpp:
+        (WebCore::HandleKeyPress::dispatch):
+        * replay/SerializationMethods.cpp:
+        (JSC::EncodingTraits&lt;NondeterministicInputBase&gt;::encodeValue):
+        (JSC::EncodingTraits&lt;NondeterministicInputBase&gt;::decodeValue): Switch existing
+        encode/decode calls to use the shortening macros.
+        (JSC::EncodingTraits&lt;KeypressCommand&gt;::encodeValue): Added.
+        (JSC::EncodingTraits&lt;KeypressCommand&gt;::decodeValue): Added.
+        (JSC::PlatformKeyboardEventAppKit::PlatformKeyboardEventAppKit): Subclass
+        PlatformKeyboardEvent so that we can set AppKit-specific members not
+        initialized through the main constructors.
+        (JSC::EncodingTraits&lt;PlatformKeyboardEvent&gt;::encodeValue): Added.
+        (JSC::EncodingTraits&lt;PlatformKeyboardEvent&gt;::decodeValue): Added.
+        * replay/SerializationMethods.h:
+        * replay/UserInputBridge.cpp:
+        (WebCore::UserInputBridge::UserInputBridge): initialize m_state inside a guard.
+        (WebCore::UserInputBridge::handleKeyEvent): fill in the implementation.
+        * replay/WebInputs.json:
+
</ins><span class="cx"> 2014-03-24  Thiago de Barros Lacerda  &lt;thiago.lacerda@openbossa.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Optimizing string construction for type error in JSNavigatorCustom.cpp
</span></span></pre></div>
<a id="trunkSourceWebCorereplayReplayInputDispatchMethodscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -53,6 +53,11 @@
</span><span class="cx">     controller.page().mainFrame().navigationScheduler().scheduleLocationChange(m_securityOrigin.get(), m_url, m_referrer, true, true);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HandleKeyPress::dispatch(ReplayController&amp; controller)
+{
+    controller.page().userInputBridge().handleKeyEvent(platformEvent(), InputSource::Synthetic);
+}
+
</ins><span class="cx"> // User interaction inputs.
</span><span class="cx"> void HandleMouseMove::dispatch(ReplayController&amp; controller)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebCorereplaySerializationMethodscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/SerializationMethods.cpp (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/SerializationMethods.cpp        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/replay/SerializationMethods.cpp        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -31,14 +31,18 @@
</span><span class="cx"> #if ENABLE(WEB_REPLAY)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AllReplayInputs.h&quot;
</span><ins>+#include &quot;PlatformKeyboardEvent.h&quot;
</ins><span class="cx"> #include &quot;PlatformMouseEvent.h&quot;
</span><span class="cx"> #include &quot;ReplayInputTypes.h&quot;
</span><span class="cx"> #include &quot;SecurityOrigin.h&quot;
</span><span class="cx"> #include &quot;URL.h&quot;
</span><ins>+#include &lt;wtf/text/Base64.h&gt;
</ins><span class="cx"> 
</span><ins>+using WebCore::KeypressCommand;
</ins><span class="cx"> using WebCore::IntPoint;
</span><span class="cx"> using WebCore::MouseButton;
</span><span class="cx"> using WebCore::PlatformEvent;
</span><ins>+using WebCore::PlatformKeyboardEvent;
</ins><span class="cx"> using WebCore::PlatformMouseEvent;
</span><span class="cx"> using WebCore::SecurityOrigin;
</span><span class="cx"> using WebCore::URL;
</span><span class="lines">@@ -58,14 +62,23 @@
</span><span class="cx">     if (!_encodedValue.get&lt;_type&gt;(ASCIILiteral(#_key), _key)) \
</span><span class="cx">         return false
</span><span class="cx"> 
</span><ins>+#define ENCODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key, _value, condition) \
+    if (condition) \
+        _encodedValue.put&lt;_type&gt;(ASCIILiteral(#_key), _value)
+
+#define DECODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    _type _key; \
+    bool _key ## WasDecoded = _encodedValue.get&lt;_type&gt;(ASCIILiteral(#_key), _key)
+
</ins><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="cx"> EncodedValue EncodingTraits&lt;NondeterministicInputBase&gt;::encodeValue(const NondeterministicInputBase&amp; input)
</span><span class="cx"> {
</span><span class="cx">     EncodedValue encodedValue = EncodedValue::createObject();
</span><span class="cx">     const AtomicString&amp; type = input.type();
</span><del>-    encodedValue.put&lt;String&gt;(ASCIILiteral(&quot;type&quot;), type.string());
</del><span class="cx"> 
</span><ins>+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, type, type.string());
+
</ins><span class="cx"> #define ENCODE_IF_TYPE_TAG_MATCHES(name) \
</span><span class="cx">     if (type == inputTypes().name) { \
</span><span class="cx">         InputTraits&lt;name&gt;::encode(encodedValue, static_cast&lt;const name&amp;&gt;(input)); \
</span><span class="lines">@@ -88,9 +101,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool EncodingTraits&lt;NondeterministicInputBase&gt;::decodeValue(EncodedValue&amp; encodedValue, std::unique_ptr&lt;NondeterministicInputBase&gt;&amp; input)
</span><span class="cx"> {
</span><del>-    String type;
-    if (!encodedValue.get&lt;String&gt;(ASCIILiteral(&quot;type&quot;), type))
-        return false;
</del><ins>+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, type);
</ins><span class="cx"> 
</span><span class="cx"> #define DECODE_IF_TYPE_TAG_MATCHES(name) \
</span><span class="cx">     if (type == inputTypes().name) { \
</span><span class="lines">@@ -118,6 +129,88 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if USE(APPKIT)
+EncodedValue EncodingTraits&lt;KeypressCommand&gt;::encodeValue(const KeypressCommand&amp; command)
+{
+    EncodedValue encodedValue = EncodedValue::createObject();
+
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, commandName, command.commandName);
+    ENCODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(encodedValue, String, text, command.text, !command.text.isEmpty());
+
+    return encodedValue;
+}
+
+bool EncodingTraits&lt;KeypressCommand&gt;::decodeValue(EncodedValue&amp; encodedValue, KeypressCommand&amp; decodedValue)
+{
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, commandName);
+    DECODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(encodedValue, String, text);
+
+    decodedValue = textWasDecoded ? KeypressCommand(commandName, text) : KeypressCommand(commandName);
+    return true;
+}
+
+class PlatformKeyboardEventAppKit : public WebCore::PlatformKeyboardEvent {
+public:
+    PlatformKeyboardEventAppKit(const PlatformKeyboardEvent&amp; event, bool handledByInputMethod, Vector&lt;KeypressCommand&gt;&amp; commands)
+        : PlatformKeyboardEvent(event)
+    {
+        m_handledByInputMethod = handledByInputMethod;
+        m_commands = commands;
+    }
+};
+#endif // USE(APPKIT)
+
+EncodedValue EncodingTraits&lt;PlatformKeyboardEvent&gt;::encodeValue(const PlatformKeyboardEvent&amp; input)
+{
+    EncodedValue encodedValue = EncodedValue::createObject();
+
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, double, timestamp, input.timestamp());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers, static_cast&lt;PlatformEvent::Modifiers&gt;(input.modifiers()));
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, text, input.text());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, unmodifiedText, input.unmodifiedText());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, keyIdentifier, input.keyIdentifier());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode, input.windowsVirtualKeyCode());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode, input.nativeVirtualKeyCode());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, macCharCode, input.macCharCode());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, autoRepeat, input.isAutoRepeat());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, keypad, input.isKeypad());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, systemKey, input.isSystemKey());
+#if USE(APPKIT)
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod, input.handledByInputMethod());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, Vector&lt;KeypressCommand&gt;, commands, input.commands());
+#endif
+    return encodedValue;
+}
+
+bool EncodingTraits&lt;PlatformKeyboardEvent&gt;::decodeValue(EncodedValue&amp; encodedValue, std::unique_ptr&lt;PlatformKeyboardEvent&gt;&amp; input)
+{
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, double, timestamp);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, text);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, unmodifiedText);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, keyIdentifier);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, macCharCode);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, autoRepeat);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, keypad);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, systemKey);
+#if USE(APPKIT)
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, Vector&lt;KeypressCommand&gt;, commands);
+#endif
+
+    PlatformKeyboardEvent platformEvent = PlatformKeyboardEvent(type, text, unmodifiedText, keyIdentifier, windowsVirtualKeyCode, nativeVirtualKeyCode, macCharCode, autoRepeat, keypad, systemKey, modifiers, timestamp);
+#if USE(APPKIT)
+    input = std::make_unique&lt;PlatformKeyboardEventAppKit&gt;(platformEvent, handledByInputMethod, commands);
+#else
+    input = std::make_unique&lt;PlatformKeyboardEvent&gt;(platformEvent);
+#endif
+    return true;
+}
+
</ins><span class="cx"> EncodedValue EncodingTraits&lt;PlatformMouseEvent&gt;::encodeValue(const PlatformMouseEvent&amp; input)
</span><span class="cx"> {
</span><span class="cx">     EncodedValue encodedValue = EncodedValue::createObject();
</span></span></pre></div>
<a id="trunkSourceWebCorereplaySerializationMethodsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/SerializationMethods.h (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/SerializationMethods.h        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/replay/SerializationMethods.h        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -32,19 +32,34 @@
</span><span class="cx"> 
</span><span class="cx"> #include &lt;replay/EncodedValue.h&gt;
</span><span class="cx"> #include &lt;replay/NondeterministicInput.h&gt;
</span><ins>+#include &lt;wtf/Vector.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> class Document;
</span><span class="cx"> class Frame;
</span><span class="cx"> class Page;
</span><ins>+class PlatformKeyboardEvent;
</ins><span class="cx"> class PlatformMouseEvent;
</span><span class="cx"> class SecurityOrigin;
</span><span class="cx"> class URL;
</span><ins>+
+#if USE(APPKIT)
+struct KeypressCommand;
+#endif
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> // Template specializations must be defined in the same namespace as the template declaration.
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><ins>+#if USE(APPKIT)
+template&lt;&gt; struct EncodingTraits&lt;WebCore::KeypressCommand&gt; {
+    typedef WebCore::KeypressCommand DecodedType;
+
+    static EncodedValue encodeValue(const WebCore::KeypressCommand&amp; value);
+    static bool decodeValue(EncodedValue&amp;, WebCore::KeypressCommand&amp; value);
+};
+#endif // USE(APPKIT)
+
</ins><span class="cx"> template&lt;&gt; struct EncodingTraits&lt;NondeterministicInputBase&gt; {
</span><span class="cx">     typedef NondeterministicInputBase DecodedType;
</span><span class="cx"> 
</span><span class="lines">@@ -52,6 +67,13 @@
</span><span class="cx">     static bool decodeValue(EncodedValue&amp;, std::unique_ptr&lt;NondeterministicInputBase&gt;&amp; value);
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+template&lt;&gt; struct EncodingTraits&lt;WebCore::PlatformKeyboardEvent&gt; {
+    typedef WebCore::PlatformKeyboardEvent DecodedType;
+
+    static EncodedValue encodeValue(const WebCore::PlatformKeyboardEvent&amp; value);
+    static bool decodeValue(EncodedValue&amp;, std::unique_ptr&lt;WebCore::PlatformKeyboardEvent&gt;&amp; value);
+};
+
</ins><span class="cx"> template&lt;&gt; struct EncodingTraits&lt;WebCore::PlatformMouseEvent&gt; {
</span><span class="cx">     typedef WebCore::PlatformMouseEvent DecodedType;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorereplayUserInputBridgecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/UserInputBridge.cpp (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/UserInputBridge.cpp        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/replay/UserInputBridge.cpp        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -55,6 +55,9 @@
</span><span class="cx"> 
</span><span class="cx"> UserInputBridge::UserInputBridge(Page&amp; page)
</span><span class="cx">     : m_page(page)
</span><ins>+#if ENABLE(WEB_REPLAY)
+    , m_state(UserInputBridge::State::Open)
+#endif
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -136,8 +139,19 @@
</span><span class="cx">     return m_page.mainFrame().eventHandler().passMouseMovedEventToScrollbars(mouseEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool UserInputBridge::handleKeyEvent(const PlatformKeyboardEvent&amp; keyEvent, InputSource)
</del><ins>+bool UserInputBridge::handleKeyEvent(const PlatformKeyboardEvent&amp; keyEvent, InputSource inputSource)
</ins><span class="cx"> {
</span><ins>+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr&lt;PlatformKeyboardEvent&gt; ownedEvent = std::make_unique&lt;PlatformKeyboardEvent&gt;(keyEvent);
+        activeCursor().appendInput&lt;HandleKeyPress&gt;(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
</ins><span class="cx">     return m_page.focusController().focusedOrMainFrame().eventHandler().keyEvent(keyEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorereplayWebInputsjson"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/WebInputs.json (166211 => 166212)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/WebInputs.json        2014-03-25 00:17:45 UTC (rev 166211)
+++ trunk/Source/WebCore/replay/WebInputs.json        2014-03-25 00:52:46 UTC (rev 166212)
</span><span class="lines">@@ -55,6 +55,10 @@
</span><span class="cx">                 &quot;header&quot;: &quot;page/Page.h&quot;
</span><span class="cx">             },
</span><span class="cx">             {
</span><ins>+                &quot;name&quot;: &quot;PlatformKeyboardEvent&quot;, &quot;mode&quot;: &quot;OWNED&quot;,
+                &quot;header&quot;: &quot;platform/PlatformKeyboardEvent.h&quot;
+            },
+            {
</ins><span class="cx">                 &quot;name&quot;: &quot;PlatformMouseEvent&quot;, &quot;mode&quot;: &quot;OWNED&quot;,
</span><span class="cx">                 &quot;header&quot;: &quot;platform/PlatformMouseEvent.h&quot;
</span><span class="cx">             },
</span><span class="lines">@@ -134,6 +138,14 @@
</span><span class="cx">             ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><ins>+            &quot;name&quot;: &quot;HandleKeyPress&quot;,
+            &quot;description&quot;: &quot;The embedder signalled a key press event.&quot;,
+            &quot;queue&quot;: &quot;EVENT_LOOP&quot;,
+            &quot;members&quot;: [
+                { &quot;name&quot;: &quot;platformEvent&quot;, &quot;type&quot;: &quot;PlatformKeyboardEvent&quot; }
+            ]
+        },
+        {
</ins><span class="cx">             &quot;name&quot;: &quot;InitialNavigation&quot;,
</span><span class="cx">             &quot;description&quot;: &quot;Initiate the initial main frame navigation.&quot;,
</span><span class="cx">             &quot;queue&quot;: &quot;EVENT_LOOP&quot;,
</span></span></pre>
</div>
</div>

</body>
</html>