<!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>[166006] 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/166006">166006</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2014-03-20 15:14:41 -0700 (Thu, 20 Mar 2014)</dd>
</dl>

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

Reviewed by Joseph Pecoraro.

.:

Create a manual test for capture/replay of mouse events.
Copy over the crypto-md5.js library from SunSpider.

* ManualTests/inspector/replay-mouse-events.html: Added.
* ManualTests/inspector/resources/crypto-md5.js: Added.

Source/WebCore:

Add support for capturing and replaying mouse inputs that come from WebKit2.
Hook up the UserInputBridge to session state changes in the ReplayController so
that the bridge knows when to capture or deny mouse inputs.

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

* platform/PlatformEvent.h: Give explicit storage types to Modifiers and Type enums
so they can be forward-declared.
* platform/PlatformMouseEvent.h: Give an explicit storage type to enum MouseButton.
Add operator== and operator!= for MouseButton to work around an MSVC bug.

* replay/ReplayController.cpp: Perform session state changes in a helper function, and
at the same time change the state of the page's user input bridge.
(WebCore::ReplayController::setSessionState):
(WebCore::ReplayController::startCapturing):
(WebCore::ReplayController::stopCapturing):
(WebCore::ReplayController::startPlayback):
(WebCore::ReplayController::cancelPlayback):

* replay/ReplayInputDispatchMethods.cpp: Add dispatch methods for new inputs.
(WebCore::HandleMouseMove::dispatch):
(WebCore::HandleMousePress::dispatch):
(WebCore::HandleMouseRelease::dispatch):

* replay/SerializationMethods.cpp: Add helper macros so that encode/decode methods look
symmetric with one data member per line. This helps reduce unintentional inconsistencies.
(JSC::EncodingTraits&lt;PlatformMouseEvent&gt;::encodeValue): Added.
(JSC::EncodingTraits&lt;PlatformMouseEvent&gt;::decodeValue): Added.
* replay/SerializationMethods.h:

* replay/UserInputBridge.cpp: Fill in the bridge method implementations for mouse
events, adding helpers and macros as necessary to reduce boilerplate.
(WebCore::UserInputBridge::activeCursor): Added.
(WebCore::UserInputBridge::handleMousePressEvent):
(WebCore::UserInputBridge::handleMouseReleaseEvent):
(WebCore::UserInputBridge::handleMouseMoveEvent):
(WebCore::UserInputBridge::handleMouseMoveOnScrollbarEvent):

* replay/UserInputBridge.h: Add a bridge state enum along with getters and setters.
The enum value controls whether the bridge should capture commands, deny non-synthetic
commands (from the user), or allow anything to pass (the default).
(WebCore::UserInputBridge::setState): Added.
(WebCore::UserInputBridge::state): Added.

* replay/WebInputs.json: Add inputs HandleMouseMove, HandleMousePress, HandleMouseRelease.
Add enum definitions for PlatformEvent::Type, PlatformEvent::Modifiers, and PlatformMouseEvent::MouseButton.
Alphabetize the existing data type definitions.</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="#trunkSourceWebCoreplatformPlatformEventh">trunk/Source/WebCore/platform/PlatformEvent.h</a></li>
<li><a href="#trunkSourceWebCoreplatformPlatformMouseEventh">trunk/Source/WebCore/platform/PlatformMouseEvent.h</a></li>
<li><a href="#trunkSourceWebCoreplatformwinPlatformMouseEventWincpp">trunk/Source/WebCore/platform/win/PlatformMouseEventWin.cpp</a></li>
<li><a href="#trunkSourceWebCorereplayReplayControllercpp">trunk/Source/WebCore/replay/ReplayController.cpp</a></li>
<li><a href="#trunkSourceWebCorereplayReplayControllerh">trunk/Source/WebCore/replay/ReplayController.h</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="#trunkSourceWebCorereplayUserInputBridgeh">trunk/Source/WebCore/replay/UserInputBridge.h</a></li>
<li><a href="#trunkSourceWebCorereplayWebInputsjson">trunk/Source/WebCore/replay/WebInputs.json</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkManualTestsinspectorreplaymouseeventshtml">trunk/ManualTests/inspector/replay-mouse-events.html</a></li>
<li><a href="#trunkManualTestsinspectorresourcescryptomd5js">trunk/ManualTests/inspector/resources/crypto-md5.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/ChangeLog (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/ChangeLog        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/ChangeLog        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2014-03-20  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Replay: capture and replay mouse events
+        https://bugs.webkit.org/show_bug.cgi?id=129395
+
+        Reviewed by Joseph Pecoraro.
+
+        Create a manual test for capture/replay of mouse events.
+        Copy over the crypto-md5.js library from SunSpider.
+
+        * ManualTests/inspector/replay-mouse-events.html: Added.
+        * ManualTests/inspector/resources/crypto-md5.js: Added.
+
</ins><span class="cx"> 2014-03-20  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK][CMake] Add support for building with Clang
</span></span></pre></div>
<a id="trunkManualTestsinspectorreplaymouseeventshtml"></a>
<div class="addfile"><h4>Added: trunk/ManualTests/inspector/replay-mouse-events.html (0 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/ManualTests/inspector/replay-mouse-events.html                                (rev 0)
+++ trunk/ManualTests/inspector/replay-mouse-events.html        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -0,0 +1,93 @@
</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;
+
+    function glyphForType(type) {
+        switch (type) {
+        case &quot;mousedown&quot;: return &quot;D&quot;;
+        case &quot;mousemove&quot;: return &quot;M&quot;;
+        case &quot;mouseup&quot;: return &quot;U&quot;;
+        case &quot;mouseover&quot;: return &quot;I&quot;;
+        case &quot;mouseout&quot;: return &quot;O&quot;;
+        case &quot;click&quot;: return &quot;C&quot;;
+        case &quot;dblclick&quot;: return &quot;2&quot;;
+        }
+    }
+
+    document.onmousedown = handleEvent;
+    document.onmousemove = handleEvent;
+    document.onmouseup = handleEvent;
+    document.onmouseover = handleEvent;
+    document.onmouseout = handleEvent;
+    document.onclick = handleEvent;
+    document.ondblclick = handleEvent;
+    
+    window.dumpedEvents = [];
+    
+    function handleEvent(event) {
+        var properties = [&quot;type&quot;, &quot;eventPhase&quot;, &quot;bubbles&quot;, &quot;cancelable&quot;, &quot;screenX&quot;, &quot;screenY&quot;, &quot;clientX&quot;, &quot;clientY&quot;, &quot;ctrlKey&quot;, &quot;shiftKey&quot;, &quot;altKey&quot;, &quot;metaKey&quot;, &quot;button&quot;];
+        obj = {};
+        for (var key of properties)
+            obj[key] = event[key];
+    
+        dumpedEvents.push(obj);
+
+        var block = createBlock(hex_md5(JSON.stringify(obj)));
+        block.textContent = glyphForType(event.type);
+        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) {
+        var color = &quot;#&quot; + hash.substr(0,6);
+        var block = document.createElement(&quot;span&quot;);
+        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 {
+    width: 20px;
+    height: 20px;
+    border-radius: 10px;
+    font-size: 18px;
+    font-weight: bold;
+    font-family: sans-serif;
+    color: #fff;
+    text-align: center;
+}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p&gt;This page is a manual test for capture and replay of mouse-related events.&lt;/p&gt;
+&lt;p&gt;Below, a block is created for each mouse event, where the color is derived from a hash of the mouse event. At the bottom is a cumulative hash of all mouse event data.&lt;/p&gt;
+&lt;hr/&gt;
+&lt;p&gt;
+To test the replay functionality, open the Web Inspector, start capturing, and then move the mouse around this test page. 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;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="trunkManualTestsinspectorresourcescryptomd5js"></a>
<div class="addfile"><h4>Added: trunk/ManualTests/inspector/resources/crypto-md5.js (0 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/ManualTests/inspector/resources/crypto-md5.js                                (rev 0)
+++ trunk/ManualTests/inspector/resources/crypto-md5.js        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -0,0 +1,256 @@
</span><ins>+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = &quot;&quot;; /* base-64 pad character. &quot;=&quot; for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5(&quot;abc&quot;) == &quot;900150983cd24fb0d6963f7d28e17f72&quot;;
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len &gt;&gt; 5] |= 0x80 &lt;&lt; ((len) % 32);
+  x[(((len + 64) &gt;&gt;&gt; 9) &lt;&lt; 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i &lt; x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b &amp; c) | ((~b) &amp; d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b &amp; d) | (c &amp; (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length &gt; 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i &lt; 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x &amp; 0xFFFF) + (y &amp; 0xFFFF);
+  var msw = (x &gt;&gt; 16) + (y &gt;&gt; 16) + (lsw &gt;&gt; 16);
+  return (msw &lt;&lt; 16) | (lsw &amp; 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num &lt;&lt; cnt) | (num &gt;&gt;&gt; (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters &gt;255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 &lt;&lt; chrsz) - 1;
+  for(var i = 0; i &lt; str.length * chrsz; i += chrsz)
+    bin[i&gt;&gt;5] |= (str.charCodeAt(i / chrsz) &amp; mask) &lt;&lt; (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = &quot;&quot;;
+  var mask = (1 &lt;&lt; chrsz) - 1;
+  for(var i = 0; i &lt; bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i&gt;&gt;5] &gt;&gt;&gt; (i % 32)) &amp; mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? &quot;0123456789ABCDEF&quot; : &quot;0123456789abcdef&quot;;
+  var str = &quot;&quot;;
+  for(var i = 0; i &lt; binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i&gt;&gt;2] &gt;&gt; ((i%4)*8+4)) &amp; 0xF) +
+           hex_tab.charAt((binarray[i&gt;&gt;2] &gt;&gt; ((i%4)*8  )) &amp; 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;;
+  var str = &quot;&quot;;
+  for(var i = 0; i &lt; binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   &gt;&gt; 2] &gt;&gt; 8 * ( i   %4)) &amp; 0xFF) &lt;&lt; 16)
+                | (((binarray[i+1 &gt;&gt; 2] &gt;&gt; 8 * ((i+1)%4)) &amp; 0xFF) &lt;&lt; 8 )
+                |  ((binarray[i+2 &gt;&gt; 2] &gt;&gt; 8 * ((i+2)%4)) &amp; 0xFF);
+    for(var j = 0; j &lt; 4; j++)
+    {
+      if(i * 8 + j * 6 &gt; binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet &gt;&gt; 6*(3-j)) &amp; 0x3F);
+    }
+  }
+  return str;
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/ChangeLog        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -1,3 +1,58 @@
</span><ins>+2014-03-20  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Replay: capture and replay mouse events
+        https://bugs.webkit.org/show_bug.cgi?id=129395
+
+        Reviewed by Joseph Pecoraro.
+
+        Add support for capturing and replaying mouse inputs that come from WebKit2.
+        Hook up the UserInputBridge to session state changes in the ReplayController so
+        that the bridge knows when to capture or deny mouse inputs.
+
+        Test: ManualTests/inspector/replay-mouse-events.html
+
+        * platform/PlatformEvent.h: Give explicit storage types to Modifiers and Type enums
+        so they can be forward-declared.
+        * platform/PlatformMouseEvent.h: Give an explicit storage type to enum MouseButton.
+        Add operator== and operator!= for MouseButton to work around an MSVC bug.
+
+        * replay/ReplayController.cpp: Perform session state changes in a helper function, and
+        at the same time change the state of the page's user input bridge.
+        (WebCore::ReplayController::setSessionState):
+        (WebCore::ReplayController::startCapturing):
+        (WebCore::ReplayController::stopCapturing):
+        (WebCore::ReplayController::startPlayback):
+        (WebCore::ReplayController::cancelPlayback):
+
+        * replay/ReplayInputDispatchMethods.cpp: Add dispatch methods for new inputs.
+        (WebCore::HandleMouseMove::dispatch):
+        (WebCore::HandleMousePress::dispatch):
+        (WebCore::HandleMouseRelease::dispatch):
+
+        * replay/SerializationMethods.cpp: Add helper macros so that encode/decode methods look
+        symmetric with one data member per line. This helps reduce unintentional inconsistencies.
+        (JSC::EncodingTraits&lt;PlatformMouseEvent&gt;::encodeValue): Added.
+        (JSC::EncodingTraits&lt;PlatformMouseEvent&gt;::decodeValue): Added.
+        * replay/SerializationMethods.h:
+
+        * replay/UserInputBridge.cpp: Fill in the bridge method implementations for mouse
+        events, adding helpers and macros as necessary to reduce boilerplate.
+        (WebCore::UserInputBridge::activeCursor): Added.
+        (WebCore::UserInputBridge::handleMousePressEvent):
+        (WebCore::UserInputBridge::handleMouseReleaseEvent):
+        (WebCore::UserInputBridge::handleMouseMoveEvent):
+        (WebCore::UserInputBridge::handleMouseMoveOnScrollbarEvent):
+
+        * replay/UserInputBridge.h: Add a bridge state enum along with getters and setters.
+        The enum value controls whether the bridge should capture commands, deny non-synthetic
+        commands (from the user), or allow anything to pass (the default).
+        (WebCore::UserInputBridge::setState): Added.
+        (WebCore::UserInputBridge::state): Added.
+
+        * replay/WebInputs.json: Add inputs HandleMouseMove, HandleMousePress, HandleMouseRelease.
+        Add enum definitions for PlatformEvent::Type, PlatformEvent::Modifiers, and PlatformMouseEvent::MouseButton.
+        Alphabetize the existing data type definitions.
+
</ins><span class="cx"> 2014-03-20  Tim Horton  &lt;timothy_horton@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add WebCore::IOSurface wrapper
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformPlatformEventh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/PlatformEvent.h (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/PlatformEvent.h        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/platform/PlatformEvent.h        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> 
</span><span class="cx"> class PlatformEvent {
</span><span class="cx"> public:
</span><del>-    enum Type {
</del><ins>+    enum Type : uint8_t {
</ins><span class="cx">         NoType = 0,
</span><span class="cx"> 
</span><span class="cx">         // PlatformKeyboardEvent
</span><span class="lines">@@ -57,7 +57,7 @@
</span><span class="cx"> #endif
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    enum Modifiers {
</del><ins>+    enum Modifiers : uint8_t {
</ins><span class="cx">         AltKey      = 1 &lt;&lt; 0,
</span><span class="cx">         CtrlKey     = 1 &lt;&lt; 1,
</span><span class="cx">         MetaKey     = 1 &lt;&lt; 2,
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformPlatformMouseEventh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/PlatformMouseEvent.h (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/PlatformMouseEvent.h        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/platform/PlatformMouseEvent.h        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -44,7 +44,7 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx">     
</span><span class="cx">     // These button numbers match the ones used in the DOM API, 0 through 2, except for NoButton which isn't specified.
</span><del>-    enum MouseButton { NoButton = -1, LeftButton, MiddleButton, RightButton };
</del><ins>+    enum MouseButton : int8_t { NoButton = -1, LeftButton, MiddleButton, RightButton };
</ins><span class="cx"> 
</span><span class="cx">     class PlatformMouseEvent : public PlatformEvent {
</span><span class="cx">     public:
</span><span class="lines">@@ -128,6 +128,13 @@
</span><span class="cx"> #endif
</span><span class="cx">     };
</span><span class="cx"> 
</span><ins>+#if PLATFORM(WIN)
+    // These methods are necessary to work around the fact that MSVC will not find a most-specific
+    // operator== to use after implicitly converting MouseButton to an unsigned short.
+    bool operator==(unsigned short a, MouseButton b);
+    bool operator!=(unsigned short a, MouseButton b);
+#endif
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // PlatformMouseEvent_h
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformwinPlatformMouseEventWincpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/win/PlatformMouseEventWin.cpp (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/win/PlatformMouseEventWin.cpp        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/platform/win/PlatformMouseEventWin.cpp        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -121,4 +121,14 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool operator==(unsigned short a, MouseButton b)
+{
+    return a == static_cast&lt;unsigned short&gt;(b);
+}
+
+bool operator!=(unsigned short a, MouseButton b)
+{
+    return a != static_cast&lt;unsigned short&gt;(b);
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorereplayReplayControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/ReplayController.cpp (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/ReplayController.cpp        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/ReplayController.cpp        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx"> #include &quot;ReplaySessionSegment.h&quot;
</span><span class="cx"> #include &quot;ReplayingInputCursor.h&quot;
</span><span class="cx"> #include &quot;ScriptController.h&quot;
</span><ins>+#include &quot;UserInputBridge.h&quot;
</ins><span class="cx"> #include &quot;WebReplayInputs.h&quot;
</span><span class="cx"> #include &lt;replay/EmptyInputCursor.h&gt;
</span><span class="cx"> #include &lt;wtf/text/CString.h&gt;
</span><span class="lines">@@ -64,6 +65,32 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ReplayController::setSessionState(SessionState state)
+{
+    ASSERT(state != m_sessionState);
+
+    switch (m_sessionState) {
+    case SessionState::Capturing:
+        ASSERT(state == SessionState::Inactive);
+
+        m_sessionState = state;
+        m_page.userInputBridge().setState(UserInputBridge::State::Capturing);
+        break;
+
+    case SessionState::Inactive:
+        m_sessionState = state;
+        m_page.userInputBridge().setState(UserInputBridge::State::Open);
+        break;
+
+    case SessionState::Replaying:
+        ASSERT(state == SessionState::Inactive);
+
+        m_sessionState = state;
+        m_page.userInputBridge().setState(UserInputBridge::State::Replaying);
+        break;
+    }
+}
+
</ins><span class="cx"> void ReplayController::switchSession(PassRefPtr&lt;ReplaySession&gt; session)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_segmentState == SegmentState::Unloaded);
</span><span class="lines">@@ -171,12 +198,13 @@
</span><span class="cx">     ASSERT(m_sessionState == SessionState::Inactive);
</span><span class="cx">     ASSERT(m_segmentState == SegmentState::Unloaded);
</span><span class="cx"> 
</span><del>-    m_sessionState = SessionState::Capturing;
</del><ins>+    setSessionState(SessionState::Capturing);
</ins><span class="cx"> 
</span><span class="cx">     LOG(WebReplay, &quot;%-20s Starting capture.\n&quot;, &quot;ReplayController&quot;);
</span><span class="cx">     InspectorInstrumentation::captureStarted(&amp;m_page);
</span><span class="cx"> 
</span><span class="cx">     m_currentPosition = ReplayPosition(0, 0);
</span><ins>+
</ins><span class="cx">     createSegment();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -187,7 +215,7 @@
</span><span class="cx"> 
</span><span class="cx">     completeSegment();
</span><span class="cx"> 
</span><del>-    m_sessionState = SessionState::Inactive;
</del><ins>+    setSessionState(SessionState::Inactive);
</ins><span class="cx"> 
</span><span class="cx">     LOG(WebReplay, &quot;%-20s Stopping capture.\n&quot;, &quot;ReplayController&quot;);
</span><span class="cx">     InspectorInstrumentation::captureStopped(&amp;m_page);
</span><span class="lines">@@ -247,7 +275,7 @@
</span><span class="cx">     m_dispatchSpeed = speed;
</span><span class="cx"> 
</span><span class="cx">     if (m_sessionState != SessionState::Replaying)
</span><del>-        m_sessionState = SessionState::Replaying;
</del><ins>+        setSessionState(SessionState::Replaying);
</ins><span class="cx"> 
</span><span class="cx">     if (m_segmentState == SegmentState::Unloaded)
</span><span class="cx">         loadSegmentAtIndex(position.segmentOffset);
</span></span></pre></div>
<a id="trunkSourceWebCorereplayReplayControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/ReplayController.h (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/ReplayController.h        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/ReplayController.h        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -150,6 +150,8 @@
</span><span class="cx"> 
</span><span class="cx">     EventLoopInputDispatcher&amp; dispatcher() const;
</span><span class="cx"> 
</span><ins>+    void setSessionState(SessionState);
+
</ins><span class="cx">     Page&amp; m_page;
</span><span class="cx"> 
</span><span class="cx">     RefPtr&lt;ReplaySessionSegment&gt; m_loadedSegment;
</span></span></pre></div>
<a id="trunkSourceWebCorereplayReplayInputDispatchMethodscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/ReplayInputDispatchMethods.cpp        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> #include &quot;Page.h&quot;
</span><span class="cx"> #include &quot;ReplayController.h&quot;
</span><span class="cx"> #include &quot;URL.h&quot;
</span><ins>+#include &quot;UserInputBridge.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -52,6 +53,25 @@
</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>+// User interaction inputs.
+void HandleMouseMove::dispatch(ReplayController&amp; controller)
+{
+    if (m_scrollbarTargeted)
+        controller.page().userInputBridge().handleMouseMoveOnScrollbarEvent(platformEvent(), InputSource::Synthetic);
+    else
+        controller.page().userInputBridge().handleMouseMoveEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMousePress::dispatch(ReplayController&amp; controller)
+{
+    controller.page().userInputBridge().handleMousePressEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMouseRelease::dispatch(ReplayController&amp; controller)
+{
+    controller.page().userInputBridge().handleMouseReleaseEvent(platformEvent(), InputSource::Synthetic);
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(WEB_REPLAY)
</span></span></pre></div>
<a id="trunkSourceWebCorereplaySerializationMethodscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/SerializationMethods.cpp (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/SerializationMethods.cpp        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/SerializationMethods.cpp        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -31,10 +31,15 @@
</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;PlatformMouseEvent.h&quot;
</ins><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><span class="cx"> 
</span><ins>+using WebCore::IntPoint;
+using WebCore::MouseButton;
+using WebCore::PlatformEvent;
+using WebCore::PlatformMouseEvent;
</ins><span class="cx"> using WebCore::SecurityOrigin;
</span><span class="cx"> using WebCore::URL;
</span><span class="cx"> using WebCore::inputTypes;
</span><span class="lines">@@ -45,6 +50,14 @@
</span><span class="cx"> WEB_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_WEBCORE_NAMESPACE)
</span><span class="cx"> #undef IMPORT_FROM_WEBCORE_NAMESPACE
</span><span class="cx"> 
</span><ins>+#define ENCODE_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key, _value) \
+    _encodedValue.put&lt;_type&gt;(ASCIILiteral(#_key), _value)
+
+#define DECODE_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    _type _key; \
+    if (!_encodedValue.get&lt;_type&gt;(ASCIILiteral(#_key), _key)) \
+        return false
+
</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="lines">@@ -105,6 +118,48 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+EncodedValue EncodingTraits&lt;PlatformMouseEvent&gt;::encodeValue(const PlatformMouseEvent&amp; input)
+{
+    EncodedValue encodedValue = EncodedValue::createObject();
+
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, positionX, input.position().x());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, positionY, input.position().y());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, globalPositionX, input.globalPosition().x());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, globalPositionY, input.globalPosition().y());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, MouseButton, button, input.button());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, clickCount, input.clickCount());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, shiftKey, input.shiftKey());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, ctrlKey, input.ctrlKey());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, altKey, input.altKey());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, metaKey, input.metaKey());
+    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, timestamp, input.timestamp());
+
+    return encodedValue;
+}
+
+bool EncodingTraits&lt;PlatformMouseEvent&gt;::decodeValue(EncodedValue&amp; encodedValue, std::unique_ptr&lt;PlatformMouseEvent&gt;&amp; input)
+{
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, positionX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, positionY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, globalPositionX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, globalPositionY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, MouseButton, button);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, clickCount);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, shiftKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, ctrlKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, altKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, metaKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, timestamp);
+
+    input = std::make_unique&lt;PlatformMouseEvent&gt;(IntPoint(positionX, positionY),
+        IntPoint(globalPositionX, globalPositionY),
+        button, type, clickCount,
+        shiftKey, ctrlKey, altKey, metaKey, timestamp);
+    return true;
+}
+
</ins><span class="cx"> EncodedValue EncodingTraits&lt;SecurityOrigin&gt;::encodeValue(RefPtr&lt;SecurityOrigin&gt; input)
</span><span class="cx"> {
</span><span class="cx">     return EncodedValue::createString(input-&gt;toString());
</span></span></pre></div>
<a id="trunkSourceWebCorereplaySerializationMethodsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/SerializationMethods.h (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/SerializationMethods.h        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/SerializationMethods.h        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> class Document;
</span><span class="cx"> class Frame;
</span><span class="cx"> class Page;
</span><ins>+class PlatformMouseEvent;
</ins><span class="cx"> class SecurityOrigin;
</span><span class="cx"> class URL;
</span><span class="cx"> } // namespace WebCore
</span><span class="lines">@@ -51,6 +52,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::PlatformMouseEvent&gt; {
+    typedef WebCore::PlatformMouseEvent DecodedType;
+
+    static EncodedValue encodeValue(const WebCore::PlatformMouseEvent&amp; value);
+    static bool decodeValue(EncodedValue&amp;, std::unique_ptr&lt;WebCore::PlatformMouseEvent&gt;&amp; value);
+};
+
</ins><span class="cx"> template&lt;&gt; struct EncodingTraits&lt;WebCore::URL&gt; {
</span><span class="cx">     typedef WebCore::URL DecodedType;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorereplayUserInputBridgecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/UserInputBridge.cpp (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/UserInputBridge.cpp        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/UserInputBridge.cpp        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -38,6 +38,19 @@
</span><span class="cx"> #include &quot;PlatformMouseEvent.h&quot;
</span><span class="cx"> #include &quot;PlatformWheelEvent.h&quot;
</span><span class="cx"> 
</span><ins>+#if ENABLE(WEB_REPLAY)
+#include &quot;ReplayController.h&quot;
+#include &quot;SerializationMethods.h&quot;
+#include &quot;WebReplayInputs.h&quot;
+#include &lt;replay/InputCursor.h&gt;
+#endif
+
+#define EARLY_RETURN_IF_SHOULD_IGNORE_INPUT \
+    do { \
+        if (inputSource == InputSource::User &amp;&amp; m_state == UserInputBridge::State::Replaying) \
+            return true; \
+    } while (false)
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> UserInputBridge::UserInputBridge(Page&amp; page)
</span><span class="lines">@@ -45,6 +58,13 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if ENABLE(WEB_REPLAY)
+InputCursor&amp; UserInputBridge::activeCursor() const
+{
+    return m_page.replayController().activeInputCursor();
+}
+#endif
+
</ins><span class="cx"> #if ENABLE(CONTEXT_MENUS)
</span><span class="cx"> bool UserInputBridge::handleContextMenuEvent(const PlatformMouseEvent&amp; mouseEvent, const Frame* frame, InputSource)
</span><span class="cx"> {
</span><span class="lines">@@ -52,23 +72,67 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent&amp; mouseEvent, InputSource)
</del><ins>+bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent&amp; mouseEvent, 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;PlatformMouseEvent&gt; ownedEvent = std::make_unique&lt;PlatformMouseEvent&gt;(mouseEvent);
+        activeCursor().appendInput&lt;HandleMousePress&gt;(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
</ins><span class="cx">     return m_page.mainFrame().eventHandler().handleMousePressEvent(mouseEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent&amp; mouseEvent, InputSource)
</del><ins>+bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent&amp; mouseEvent, 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;PlatformMouseEvent&gt; ownedEvent = std::make_unique&lt;PlatformMouseEvent&gt;(mouseEvent);
+        activeCursor().appendInput&lt;HandleMouseRelease&gt;(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
</ins><span class="cx">     return m_page.mainFrame().eventHandler().handleMouseReleaseEvent(mouseEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent&amp; mouseEvent, InputSource)
</del><ins>+bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent&amp; mouseEvent, 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;PlatformMouseEvent&gt; ownedEvent = std::make_unique&lt;PlatformMouseEvent&gt;(mouseEvent);
+        activeCursor().appendInput&lt;HandleMouseMove&gt;(std::move(ownedEvent), false);
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
</ins><span class="cx">     return m_page.mainFrame().eventHandler().mouseMoved(mouseEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent&amp; mouseEvent, InputSource)
</del><ins>+bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent&amp; mouseEvent, 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;PlatformMouseEvent&gt; ownedEvent = std::make_unique&lt;PlatformMouseEvent&gt;(mouseEvent);
+        activeCursor().appendInput&lt;HandleMouseMove&gt;(std::move(ownedEvent), true);
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
</ins><span class="cx">     return m_page.mainFrame().eventHandler().passMouseMovedEventToScrollbars(mouseEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorereplayUserInputBridgeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/UserInputBridge.h (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/UserInputBridge.h        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/UserInputBridge.h        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -31,6 +31,10 @@
</span><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &lt;wtf/Noncopyable.h&gt;
</span><span class="cx"> 
</span><ins>+namespace JSC {
+class InputCursor;
+}
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> struct FrameLoadRequest;
</span><span class="lines">@@ -53,6 +57,19 @@
</span><span class="cx"> public:
</span><span class="cx">     UserInputBridge(Page&amp;);
</span><span class="cx"> 
</span><ins>+#if ENABLE(WEB_REPLAY)
+    enum class State {
+        Capturing,
+        Open,
+        Replaying,
+    };
+
+    void setState(State bridgeState) { m_state = bridgeState; }
+    State state() const { return m_state; }
+
+    JSC::InputCursor&amp; activeCursor() const;
+#endif
+
</ins><span class="cx">     // User input APIs.
</span><span class="cx"> #if ENABLE(CONTEXT_MENUS)
</span><span class="cx">     bool handleContextMenuEvent(const PlatformMouseEvent&amp;, const Frame*, InputSource source = InputSource::User);
</span><span class="lines">@@ -77,6 +94,9 @@
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     Page&amp; m_page;
</span><ins>+#if ENABLE(WEB_REPLAY)
+    State m_state;
+#endif
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorereplayWebInputsjson"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/replay/WebInputs.json (166005 => 166006)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/replay/WebInputs.json        2014-03-20 22:06:38 UTC (rev 166005)
+++ trunk/Source/WebCore/replay/WebInputs.json        2014-03-20 22:14:41 UTC (rev 166006)
</span><span class="lines">@@ -27,10 +27,6 @@
</span><span class="cx"> 
</span><span class="cx">         &quot;WebCore&quot;: [
</span><span class="cx">             {
</span><del>-                &quot;name&quot;: &quot;URL&quot;, &quot;mode&quot;: &quot;HEAVY_SCALAR&quot;,
-                &quot;header&quot;: &quot;platform/URL.h&quot;
-            },
-            {
</del><span class="cx">                 &quot;name&quot;: &quot;EncodedCType&quot;, &quot;mode&quot;: &quot;SCALAR&quot;, &quot;storage&quot;: &quot;uint8_t&quot;,
</span><span class="cx">                 &quot;flags&quot;: [&quot;ENUM_CLASS&quot;],
</span><span class="cx">                 &quot;values&quot;: [
</span><span class="lines">@@ -42,12 +38,59 @@
</span><span class="cx">                 &quot;header&quot;: &quot;replay/MemoizedDOMResult.h&quot;
</span><span class="cx">             },
</span><span class="cx">             {
</span><ins>+                &quot;name&quot;: &quot;Modifiers&quot;, &quot;mode&quot;: &quot;SCALAR&quot;, &quot;storage&quot;: &quot;uint8_t&quot;,
+                &quot;enclosing_class&quot;: &quot;PlatformEvent&quot;,
+                &quot;flags&quot;: [&quot;ENUM&quot;],
+                &quot;values&quot;: [&quot;AltKey&quot;, &quot;CtrlKey&quot;, &quot;MetaKey&quot;, &quot;ShiftKey&quot;],
+                &quot;header&quot;: &quot;platform/PlatformEvent.h&quot;
+            },
+            {
+                &quot;name&quot;: &quot;MouseButton&quot;, &quot;mode&quot;: &quot;SCALAR&quot;, &quot;storage&quot;: &quot;int8_t&quot;,
+                &quot;flags&quot;: [&quot;ENUM&quot;],
+                &quot;values&quot;: [&quot;NoButton&quot;, &quot;LeftButton&quot;, &quot;MiddleButton&quot;, &quot;RightButton&quot;],
+                &quot;header&quot;: &quot;platform/PlatformMouseEvent.h&quot;
+            },
+            {
+                &quot;name&quot;: &quot;Page&quot;, &quot;mode&quot;: &quot;OWNED&quot;,
+                &quot;header&quot;: &quot;page/Page.h&quot;
+            },
+            {
+                &quot;name&quot;: &quot;PlatformMouseEvent&quot;, &quot;mode&quot;: &quot;OWNED&quot;,
+                &quot;header&quot;: &quot;platform/PlatformMouseEvent.h&quot;
+            },
+            {
</ins><span class="cx">                 &quot;name&quot;: &quot;SecurityOrigin&quot;, &quot;mode&quot;: &quot;SHARED&quot;,
</span><span class="cx">                 &quot;header&quot;: &quot;page/SecurityOrigin.h&quot;
</span><span class="cx">             },
</span><span class="cx">             {
</span><del>-                &quot;name&quot;: &quot;Page&quot;, &quot;mode&quot;: &quot;OWNED&quot;,
-                &quot;header&quot;: &quot;page/Page.h&quot;
</del><ins>+                &quot;name&quot;: &quot;Type&quot;, &quot;mode&quot;: &quot;SCALAR&quot;, &quot;storage&quot;: &quot;uint8_t&quot;,
+                &quot;enclosing_class&quot;: &quot;PlatformEvent&quot;,
+                &quot;flags&quot;: [&quot;ENUM&quot;],
+                &quot;values&quot;: [
+                    &quot;NoType&quot;,
+                    &quot;KeyDown&quot;,
+                    &quot;KeyUp&quot;,
+                    &quot;RawKeyDown&quot;,
+                    &quot;Char&quot;,
+                    &quot;MouseMoved&quot;,
+                    &quot;MousePressed&quot;,
+                    &quot;MouseReleased&quot;,
+                    &quot;MouseScroll&quot;,
+                    &quot;Wheel&quot;
+                ],
+                &quot;guarded_values&quot;: {
+                    &quot;ENABLE(TOUCH_EVENTS)&quot;: [
+                        &quot;TouchStart&quot;,
+                        &quot;TouchMove&quot;,
+                        &quot;TouchEnd&quot;,
+                        &quot;TouchCancel&quot;
+                    ]
+                },
+                &quot;header&quot;: &quot;platform/PlatformEvent.h&quot;
+            },
+            {
+                &quot;name&quot;: &quot;URL&quot;, &quot;mode&quot;: &quot;HEAVY_SCALAR&quot;,
+                &quot;header&quot;: &quot;platform/URL.h&quot;
</ins><span class="cx">             }
</span><span class="cx">         ]
</span><span class="cx">     },
</span><span class="lines">@@ -66,6 +109,31 @@
</span><span class="cx">             &quot;members&quot;: [ ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><ins>+            &quot;name&quot;: &quot;HandleMouseMove&quot;,
+            &quot;description&quot;: &quot;The embedder signalled a mouse move event.&quot;,
+            &quot;queue&quot;: &quot;EVENT_LOOP&quot;,
+            &quot;members&quot;: [
+                { &quot;name&quot;: &quot;platformEvent&quot;, &quot;type&quot;: &quot;PlatformMouseEvent&quot; },
+                { &quot;name&quot;: &quot;scrollbarTargeted&quot;, &quot;type&quot;: &quot;bool&quot; }
+            ]
+        },
+        {
+            &quot;name&quot;: &quot;HandleMousePress&quot;,
+            &quot;description&quot;: &quot;The embedder signalled a mouse press event.&quot;,
+            &quot;queue&quot;: &quot;EVENT_LOOP&quot;,
+            &quot;members&quot;: [
+                { &quot;name&quot;: &quot;platformEvent&quot;, &quot;type&quot;: &quot;PlatformMouseEvent&quot; }
+            ]
+        },
+        {
+            &quot;name&quot;: &quot;HandleMouseRelease&quot;,
+            &quot;description&quot;: &quot;The embedder signalled a mouse release event.&quot;,
+            &quot;queue&quot;: &quot;EVENT_LOOP&quot;,
+            &quot;members&quot;: [
+                { &quot;name&quot;: &quot;platformEvent&quot;, &quot;type&quot;: &quot;PlatformMouseEvent&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>