Web Replay: capture and replay mouse events
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Mar 2014 22:14:41 +0000 (22:14 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Mar 2014 22:14:41 +0000 (22:14 +0000)
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<PlatformMouseEvent>::encodeValue): Added.
(JSC::EncodingTraits<PlatformMouseEvent>::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.

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

15 files changed:
ChangeLog
ManualTests/inspector/replay-mouse-events.html [new file with mode: 0644]
ManualTests/inspector/resources/crypto-md5.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/PlatformEvent.h
Source/WebCore/platform/PlatformMouseEvent.h
Source/WebCore/platform/win/PlatformMouseEventWin.cpp
Source/WebCore/replay/ReplayController.cpp
Source/WebCore/replay/ReplayController.h
Source/WebCore/replay/ReplayInputDispatchMethods.cpp
Source/WebCore/replay/SerializationMethods.cpp
Source/WebCore/replay/SerializationMethods.h
Source/WebCore/replay/UserInputBridge.cpp
Source/WebCore/replay/UserInputBridge.h
Source/WebCore/replay/WebInputs.json

index 6985a2d..501e34e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2014-03-20  Brian Burg  <bburg@apple.com>
+
+        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.
+
 2014-03-20  Zan Dobersek  <zdobersek@igalia.com>
 
         [GTK][CMake] Add support for building with Clang
 2014-03-20  Zan Dobersek  <zdobersek@igalia.com>
 
         [GTK][CMake] Add support for building with Clang
diff --git a/ManualTests/inspector/replay-mouse-events.html b/ManualTests/inspector/replay-mouse-events.html
new file mode 100644 (file)
index 0000000..3f1d8ec
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+<head>
+<script src="./resources/crypto-md5.js"></script>
+<script type="text/javascript" language="javascript" charset="utf-8">
+
+    function glyphForType(type) {
+        switch (type) {
+        case "mousedown": return "D";
+        case "mousemove": return "M";
+        case "mouseup": return "U";
+        case "mouseover": return "I";
+        case "mouseout": return "O";
+        case "click": return "C";
+        case "dblclick": return "2";
+        }
+    }
+
+    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 = ["type", "eventPhase", "bubbles", "cancelable", "screenX", "screenY", "clientX", "clientY", "ctrlKey", "shiftKey", "altKey", "metaKey", "button"];
+        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("blocks");
+        blocksContainer.appendChild(block);
+        
+        var hashLabel = document.getElementById("hash");
+        hash.textContent = hex_md5(JSON.stringify(dumpedEvents));
+    }
+    
+    function createBlock(hash) {
+        var color = "#" + hash.substr(0,6);
+        var block = document.createElement("span");
+        block.style.backgroundColor = color;
+        return block;
+    }
+    
+    function stateHash() {
+        return hex_md5(JSON.stringify(dumpedEvents));
+    }
+    
+</script>
+
+<style type="text/css">
+body {
+    max-width: 800px;
+}
+#blocks {
+    display: -webkit-flex;
+    width: 600px;
+    -webkit-flex-flow: row wrap;
+}
+    
+#blocks > span {
+    width: 20px;
+    height: 20px;
+    border-radius: 10px;
+    font-size: 18px;
+    font-weight: bold;
+    font-family: sans-serif;
+    color: #fff;
+    text-align: center;
+}
+</style>
+</head>
+<body>
+<p>This page is a manual test for capture and replay of mouse-related events.</p>
+<p>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.</p>
+<hr/>
+<p>
+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.</p>
+</p>
+<hr/>
+<div id="hash"></div>
+<div id="blocks"></div>
+</body>
+</html>
diff --git a/ManualTests/inspector/resources/crypto-md5.js b/ManualTests/inspector/resources/crypto-md5.js
new file mode 100644 (file)
index 0000000..46d2aab
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * 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  = ""; /* base-64 pad character. "=" 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("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < 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 & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~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 > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 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 & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}
index 05bae80..906f956 100644 (file)
@@ -1,3 +1,58 @@
+2014-03-20  Brian Burg  <bburg@apple.com>
+
+        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<PlatformMouseEvent>::encodeValue): Added.
+        (JSC::EncodingTraits<PlatformMouseEvent>::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.
+
 2014-03-20  Tim Horton  <timothy_horton@apple.com>
 
         Add WebCore::IOSurface wrapper
 2014-03-20  Tim Horton  <timothy_horton@apple.com>
 
         Add WebCore::IOSurface wrapper
index 533a924..bffdb82 100644 (file)
@@ -30,7 +30,7 @@ namespace WebCore {
 
 class PlatformEvent {
 public:
 
 class PlatformEvent {
 public:
-    enum Type {
+    enum Type : uint8_t {
         NoType = 0,
 
         // PlatformKeyboardEvent
         NoType = 0,
 
         // PlatformKeyboardEvent
@@ -57,7 +57,7 @@ public:
 #endif
     };
 
 #endif
     };
 
-    enum Modifiers {
+    enum Modifiers : uint8_t {
         AltKey      = 1 << 0,
         CtrlKey     = 1 << 1,
         MetaKey     = 1 << 2,
         AltKey      = 1 << 0,
         CtrlKey     = 1 << 1,
         MetaKey     = 1 << 2,
index bde273f..4da6319 100644 (file)
@@ -44,7 +44,7 @@ typedef struct _Evas_Event_Mouse_Move Evas_Event_Mouse_Move;
 namespace WebCore {
     
     // These button numbers match the ones used in the DOM API, 0 through 2, except for NoButton which isn't specified.
 namespace WebCore {
     
     // These button numbers match the ones used in the DOM API, 0 through 2, except for NoButton which isn't specified.
-    enum MouseButton { NoButton = -1, LeftButton, MiddleButton, RightButton };
+    enum MouseButton : int8_t { NoButton = -1, LeftButton, MiddleButton, RightButton };
 
     class PlatformMouseEvent : public PlatformEvent {
     public:
 
     class PlatformMouseEvent : public PlatformEvent {
     public:
@@ -128,6 +128,13 @@ namespace WebCore {
 #endif
     };
 
 #endif
     };
 
+#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
+
 } // namespace WebCore
 
 #endif // PlatformMouseEvent_h
 } // namespace WebCore
 
 #endif // PlatformMouseEvent_h
index e6ac149..143bbaa 100644 (file)
@@ -121,4 +121,14 @@ PlatformMouseEvent::PlatformMouseEvent(HWND hWnd, UINT message, WPARAM wParam, L
     }
 }
 
     }
 }
 
+bool operator==(unsigned short a, MouseButton b)
+{
+    return a == static_cast<unsigned short>(b);
+}
+
+bool operator!=(unsigned short a, MouseButton b)
+{
+    return a != static_cast<unsigned short>(b);
+}
+
 } // namespace WebCore
 } // namespace WebCore
index fe8472b..056ef07 100644 (file)
@@ -44,6 +44,7 @@
 #include "ReplaySessionSegment.h"
 #include "ReplayingInputCursor.h"
 #include "ScriptController.h"
 #include "ReplaySessionSegment.h"
 #include "ReplayingInputCursor.h"
 #include "ScriptController.h"
+#include "UserInputBridge.h"
 #include "WebReplayInputs.h"
 #include <replay/EmptyInputCursor.h>
 #include <wtf/text/CString.h>
 #include "WebReplayInputs.h"
 #include <replay/EmptyInputCursor.h>
 #include <wtf/text/CString.h>
@@ -64,6 +65,32 @@ ReplayController::ReplayController(Page& page)
 {
 }
 
 {
 }
 
+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;
+    }
+}
+
 void ReplayController::switchSession(PassRefPtr<ReplaySession> session)
 {
     ASSERT(m_segmentState == SegmentState::Unloaded);
 void ReplayController::switchSession(PassRefPtr<ReplaySession> session)
 {
     ASSERT(m_segmentState == SegmentState::Unloaded);
@@ -171,12 +198,13 @@ void ReplayController::startCapturing()
     ASSERT(m_sessionState == SessionState::Inactive);
     ASSERT(m_segmentState == SegmentState::Unloaded);
 
     ASSERT(m_sessionState == SessionState::Inactive);
     ASSERT(m_segmentState == SegmentState::Unloaded);
 
-    m_sessionState = SessionState::Capturing;
+    setSessionState(SessionState::Capturing);
 
     LOG(WebReplay, "%-20s Starting capture.\n", "ReplayController");
     InspectorInstrumentation::captureStarted(&m_page);
 
     m_currentPosition = ReplayPosition(0, 0);
 
     LOG(WebReplay, "%-20s Starting capture.\n", "ReplayController");
     InspectorInstrumentation::captureStarted(&m_page);
 
     m_currentPosition = ReplayPosition(0, 0);
+
     createSegment();
 }
 
     createSegment();
 }
 
@@ -187,7 +215,7 @@ void ReplayController::stopCapturing()
 
     completeSegment();
 
 
     completeSegment();
 
-    m_sessionState = SessionState::Inactive;
+    setSessionState(SessionState::Inactive);
 
     LOG(WebReplay, "%-20s Stopping capture.\n", "ReplayController");
     InspectorInstrumentation::captureStopped(&m_page);
 
     LOG(WebReplay, "%-20s Stopping capture.\n", "ReplayController");
     InspectorInstrumentation::captureStopped(&m_page);
@@ -247,7 +275,7 @@ void ReplayController::replayToPosition(const ReplayPosition& position, Dispatch
     m_dispatchSpeed = speed;
 
     if (m_sessionState != SessionState::Replaying)
     m_dispatchSpeed = speed;
 
     if (m_sessionState != SessionState::Replaying)
-        m_sessionState = SessionState::Replaying;
+        setSessionState(SessionState::Replaying);
 
     if (m_segmentState == SegmentState::Unloaded)
         loadSegmentAtIndex(position.segmentOffset);
 
     if (m_segmentState == SegmentState::Unloaded)
         loadSegmentAtIndex(position.segmentOffset);
index af733d9..ca8cbd6 100644 (file)
@@ -150,6 +150,8 @@ private:
 
     EventLoopInputDispatcher& dispatcher() const;
 
 
     EventLoopInputDispatcher& dispatcher() const;
 
+    void setSessionState(SessionState);
+
     Page& m_page;
 
     RefPtr<ReplaySessionSegment> m_loadedSegment;
     Page& m_page;
 
     RefPtr<ReplaySessionSegment> m_loadedSegment;
index 14f76ce..3855225 100644 (file)
@@ -34,6 +34,7 @@
 #include "Page.h"
 #include "ReplayController.h"
 #include "URL.h"
 #include "Page.h"
 #include "ReplayController.h"
 #include "URL.h"
+#include "UserInputBridge.h"
 
 namespace WebCore {
 
 
 namespace WebCore {
 
@@ -52,6 +53,25 @@ void InitialNavigation::dispatch(ReplayController& controller)
     controller.page().mainFrame().navigationScheduler().scheduleLocationChange(m_securityOrigin.get(), m_url, m_referrer, true, true);
 }
 
     controller.page().mainFrame().navigationScheduler().scheduleLocationChange(m_securityOrigin.get(), m_url, m_referrer, true, true);
 }
 
+// User interaction inputs.
+void HandleMouseMove::dispatch(ReplayController& controller)
+{
+    if (m_scrollbarTargeted)
+        controller.page().userInputBridge().handleMouseMoveOnScrollbarEvent(platformEvent(), InputSource::Synthetic);
+    else
+        controller.page().userInputBridge().handleMouseMoveEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMousePress::dispatch(ReplayController& controller)
+{
+    controller.page().userInputBridge().handleMousePressEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMouseRelease::dispatch(ReplayController& controller)
+{
+    controller.page().userInputBridge().handleMouseReleaseEvent(platformEvent(), InputSource::Synthetic);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WEB_REPLAY)
 } // namespace WebCore
 
 #endif // ENABLE(WEB_REPLAY)
index 1f15043..e860984 100644 (file)
 #if ENABLE(WEB_REPLAY)
 
 #include "AllReplayInputs.h"
 #if ENABLE(WEB_REPLAY)
 
 #include "AllReplayInputs.h"
+#include "PlatformMouseEvent.h"
 #include "ReplayInputTypes.h"
 #include "SecurityOrigin.h"
 #include "URL.h"
 
 #include "ReplayInputTypes.h"
 #include "SecurityOrigin.h"
 #include "URL.h"
 
+using WebCore::IntPoint;
+using WebCore::MouseButton;
+using WebCore::PlatformEvent;
+using WebCore::PlatformMouseEvent;
 using WebCore::SecurityOrigin;
 using WebCore::URL;
 using WebCore::inputTypes;
 using WebCore::SecurityOrigin;
 using WebCore::URL;
 using WebCore::inputTypes;
@@ -45,6 +50,14 @@ using WebCore::name; \
 WEB_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_WEBCORE_NAMESPACE)
 #undef IMPORT_FROM_WEBCORE_NAMESPACE
 
 WEB_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_WEBCORE_NAMESPACE)
 #undef IMPORT_FROM_WEBCORE_NAMESPACE
 
+#define ENCODE_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key, _value) \
+    _encodedValue.put<_type>(ASCIILiteral(#_key), _value)
+
+#define DECODE_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    _type _key; \
+    if (!_encodedValue.get<_type>(ASCIILiteral(#_key), _key)) \
+        return false
+
 namespace JSC {
 
 EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const NondeterministicInputBase& input)
 namespace JSC {
 
 EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const NondeterministicInputBase& input)
@@ -105,6 +118,48 @@ bool EncodingTraits<NondeterministicInputBase>::decodeValue(EncodedValue& encode
     return false;
 }
 
     return false;
 }
 
+EncodedValue EncodingTraits<PlatformMouseEvent>::encodeValue(const PlatformMouseEvent& 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<PlatformMouseEvent>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<PlatformMouseEvent>& 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<PlatformMouseEvent>(IntPoint(positionX, positionY),
+        IntPoint(globalPositionX, globalPositionY),
+        button, type, clickCount,
+        shiftKey, ctrlKey, altKey, metaKey, timestamp);
+    return true;
+}
+
 EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input)
 {
     return EncodedValue::createString(input->toString());
 EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input)
 {
     return EncodedValue::createString(input->toString());
index 8e112b8..839c45c 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 class Document;
 class Frame;
 class Page;
 class Document;
 class Frame;
 class Page;
+class PlatformMouseEvent;
 class SecurityOrigin;
 class URL;
 } // namespace WebCore
 class SecurityOrigin;
 class URL;
 } // namespace WebCore
@@ -51,6 +52,13 @@ template<> struct EncodingTraits<NondeterministicInputBase> {
     static bool decodeValue(EncodedValue&, std::unique_ptr<NondeterministicInputBase>& value);
 };
 
     static bool decodeValue(EncodedValue&, std::unique_ptr<NondeterministicInputBase>& value);
 };
 
+template<> struct EncodingTraits<WebCore::PlatformMouseEvent> {
+    typedef WebCore::PlatformMouseEvent DecodedType;
+
+    static EncodedValue encodeValue(const WebCore::PlatformMouseEvent& value);
+    static bool decodeValue(EncodedValue&, std::unique_ptr<WebCore::PlatformMouseEvent>& value);
+};
+
 template<> struct EncodingTraits<WebCore::URL> {
     typedef WebCore::URL DecodedType;
 
 template<> struct EncodingTraits<WebCore::URL> {
     typedef WebCore::URL DecodedType;
 
index ab02421..30a0e6c 100644 (file)
 #include "PlatformMouseEvent.h"
 #include "PlatformWheelEvent.h"
 
 #include "PlatformMouseEvent.h"
 #include "PlatformWheelEvent.h"
 
+#if ENABLE(WEB_REPLAY)
+#include "ReplayController.h"
+#include "SerializationMethods.h"
+#include "WebReplayInputs.h"
+#include <replay/InputCursor.h>
+#endif
+
+#define EARLY_RETURN_IF_SHOULD_IGNORE_INPUT \
+    do { \
+        if (inputSource == InputSource::User && m_state == UserInputBridge::State::Replaying) \
+            return true; \
+    } while (false)
+
 namespace WebCore {
 
 UserInputBridge::UserInputBridge(Page& page)
 namespace WebCore {
 
 UserInputBridge::UserInputBridge(Page& page)
@@ -45,6 +58,13 @@ UserInputBridge::UserInputBridge(Page& page)
 {
 }
 
 {
 }
 
+#if ENABLE(WEB_REPLAY)
+InputCursor& UserInputBridge::activeCursor() const
+{
+    return m_page.replayController().activeInputCursor();
+}
+#endif
+
 #if ENABLE(CONTEXT_MENUS)
 bool UserInputBridge::handleContextMenuEvent(const PlatformMouseEvent& mouseEvent, const Frame* frame, InputSource)
 {
 #if ENABLE(CONTEXT_MENUS)
 bool UserInputBridge::handleContextMenuEvent(const PlatformMouseEvent& mouseEvent, const Frame* frame, InputSource)
 {
@@ -52,23 +72,67 @@ bool UserInputBridge::handleContextMenuEvent(const PlatformMouseEvent& mouseEven
 }
 #endif
 
 }
 #endif
 
-bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent& mouseEvent, InputSource)
+bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
 {
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+        activeCursor().appendInput<HandleMousePress>(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.mainFrame().eventHandler().handleMousePressEvent(mouseEvent);
 }
 
     return m_page.mainFrame().eventHandler().handleMousePressEvent(mouseEvent);
 }
 
-bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent, InputSource)
+bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
 {
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+        activeCursor().appendInput<HandleMouseRelease>(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.mainFrame().eventHandler().handleMouseReleaseEvent(mouseEvent);
 }
 
     return m_page.mainFrame().eventHandler().handleMouseReleaseEvent(mouseEvent);
 }
 
-bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, InputSource)
+bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
 {
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+        activeCursor().appendInput<HandleMouseMove>(std::move(ownedEvent), false);
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.mainFrame().eventHandler().mouseMoved(mouseEvent);
 }
 
     return m_page.mainFrame().eventHandler().mouseMoved(mouseEvent);
 }
 
-bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent& mouseEvent, InputSource)
+bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
 {
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+        activeCursor().appendInput<HandleMouseMove>(std::move(ownedEvent), true);
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.mainFrame().eventHandler().passMouseMovedEventToScrollbars(mouseEvent);
 }
 
     return m_page.mainFrame().eventHandler().passMouseMovedEventToScrollbars(mouseEvent);
 }
 
index b1160b2..d9c7650 100644 (file)
 #include "ScrollTypes.h"
 #include <wtf/Noncopyable.h>
 
 #include "ScrollTypes.h"
 #include <wtf/Noncopyable.h>
 
+namespace JSC {
+class InputCursor;
+}
+
 namespace WebCore {
 
 struct FrameLoadRequest;
 namespace WebCore {
 
 struct FrameLoadRequest;
@@ -53,6 +57,19 @@ class UserInputBridge {
 public:
     UserInputBridge(Page&);
 
 public:
     UserInputBridge(Page&);
 
+#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& activeCursor() const;
+#endif
+
     // User input APIs.
 #if ENABLE(CONTEXT_MENUS)
     bool handleContextMenuEvent(const PlatformMouseEvent&, const Frame*, InputSource source = InputSource::User);
     // User input APIs.
 #if ENABLE(CONTEXT_MENUS)
     bool handleContextMenuEvent(const PlatformMouseEvent&, const Frame*, InputSource source = InputSource::User);
@@ -77,6 +94,9 @@ public:
 
 private:
     Page& m_page;
 
 private:
     Page& m_page;
+#if ENABLE(WEB_REPLAY)
+    State m_state;
+#endif
 };
 
 } // namespace WebCore
 };
 
 } // namespace WebCore
index 28f0d7b..6c32283 100644 (file)
 
         "WebCore": [
             {
 
         "WebCore": [
             {
-                "name": "URL", "mode": "HEAVY_SCALAR",
-                "header": "platform/URL.h"
-            },
-            {
                 "name": "EncodedCType", "mode": "SCALAR", "storage": "uint8_t",
                 "flags": ["ENUM_CLASS"],
                 "values": [
                 "name": "EncodedCType", "mode": "SCALAR", "storage": "uint8_t",
                 "flags": ["ENUM_CLASS"],
                 "values": [
                 "header": "replay/MemoizedDOMResult.h"
             },
             {
                 "header": "replay/MemoizedDOMResult.h"
             },
             {
-                "name": "SecurityOrigin", "mode": "SHARED",
-                "header": "page/SecurityOrigin.h"
+                "name": "Modifiers", "mode": "SCALAR", "storage": "uint8_t",
+                "enclosing_class": "PlatformEvent",
+                "flags": ["ENUM"],
+                "values": ["AltKey", "CtrlKey", "MetaKey", "ShiftKey"],
+                "header": "platform/PlatformEvent.h"
+            },
+            {
+                "name": "MouseButton", "mode": "SCALAR", "storage": "int8_t",
+                "flags": ["ENUM"],
+                "values": ["NoButton", "LeftButton", "MiddleButton", "RightButton"],
+                "header": "platform/PlatformMouseEvent.h"
             },
             {
                 "name": "Page", "mode": "OWNED",
                 "header": "page/Page.h"
             },
             {
                 "name": "Page", "mode": "OWNED",
                 "header": "page/Page.h"
+            },
+            {
+                "name": "PlatformMouseEvent", "mode": "OWNED",
+                "header": "platform/PlatformMouseEvent.h"
+            },
+            {
+                "name": "SecurityOrigin", "mode": "SHARED",
+                "header": "page/SecurityOrigin.h"
+            },
+            {
+                "name": "Type", "mode": "SCALAR", "storage": "uint8_t",
+                "enclosing_class": "PlatformEvent",
+                "flags": ["ENUM"],
+                "values": [
+                    "NoType",
+                    "KeyDown",
+                    "KeyUp",
+                    "RawKeyDown",
+                    "Char",
+                    "MouseMoved",
+                    "MousePressed",
+                    "MouseReleased",
+                    "MouseScroll",
+                    "Wheel"
+                ],
+                "guarded_values": {
+                    "ENABLE(TOUCH_EVENTS)": [
+                        "TouchStart",
+                        "TouchMove",
+                        "TouchEnd",
+                        "TouchCancel"
+                    ]
+                },
+                "header": "platform/PlatformEvent.h"
+            },
+            {
+                "name": "URL", "mode": "HEAVY_SCALAR",
+                "header": "platform/URL.h"
             }
         ]
     },
             }
         ]
     },
             "members": [ ]
         },
         {
             "members": [ ]
         },
         {
+            "name": "HandleMouseMove",
+            "description": "The embedder signalled a mouse move event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "platformEvent", "type": "PlatformMouseEvent" },
+                { "name": "scrollbarTargeted", "type": "bool" }
+            ]
+        },
+        {
+            "name": "HandleMousePress",
+            "description": "The embedder signalled a mouse press event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "platformEvent", "type": "PlatformMouseEvent" }
+            ]
+        },
+        {
+            "name": "HandleMouseRelease",
+            "description": "The embedder signalled a mouse release event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "platformEvent", "type": "PlatformMouseEvent" }
+            ]
+        },
+        {
             "name": "InitialNavigation",
             "description": "Initiate the initial main frame navigation.",
             "queue": "EVENT_LOOP",
             "name": "InitialNavigation",
             "description": "Initiate the initial main frame navigation.",
             "queue": "EVENT_LOOP",