Web Replay: capture and replay wheel events and scroll commands
authorburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Apr 2014 23:14:15 +0000 (23:14 +0000)
committerburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Apr 2014 23:14:15 +0000 (23:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=129402

.:

Reviewed by Timothy Hatcher.

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

Source/WebCore:

Reviewed by Timothy Hatcher and Simon Fraser.

To capture and replay scrolling, the scrolling coordinator will force synchronous
scrolling during capture and replay. If the page is capturing or replaying,
ForceOnMainThread will be added to the coordinator's SynchronousScrollingReasons.

A callback was added to signal that replay session state have changed,
and thus the synchronous scrolling reasons should be recomputed.

Automated replay reftests for scrolling are not included, because they will be
too flaky until more nondeterminism is handled. Specifically, resource loading,
initial focus/active state, and parsing are known blocking issues.

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

* page/scrolling/ScrollingCoordinator.cpp: Add a new callback for replay state
session changes. Add ForceOnMainThread if the page's active input cursor is
capturing or replaying.

(WebCore::ScrollingCoordinator::synchronousScrollingReasons):
(WebCore::ScrollingCoordinator::replaySessionStateDidChange): Added.
* page/scrolling/ScrollingCoordinator.h:

* platform/PlatformWheelEvent.h:
* platform/ScrollTypes.h: Add explicit enum storage types so these enums can
be forward-declared. This is necessary to generate enum encode/decode implementations.

* replay/ReplayController.cpp:
(WebCore::ReplayController::setForceDeterministicSettings): If async scrolling
support is available, tell the scrolling tree to behave deterministically.

* replay/ReplayInputDispatchMethods.cpp:
(WebCore::HandleWheelEvent::dispatch): Added.
(WebCore::LogicalScrollPage::dispatch): Added.
(WebCore::ScrollPage::dispatch): Added.
* replay/SerializationMethods.cpp: Introduce more specific macros for values
deserialized to arbitraray lvalues, scalars, RefPtr, and unique_ptr. Fix existing
uses of decode macros.

(JSC::EncodingTraits<NondeterministicInputBase>::encodeValue): Fix macro name.
(JSC::EncodingTraits<KeypressCommand>::encodeValue): Fix macro name.
(JSC::EncodingTraits<PlatformKeyboardEvent>::encodeValue): Fix macro name.
(JSC::EncodingTraits<PlatformMouseEvent>::encodeValue): Fix macro name.
(JSC::PlatformWheelEventCocoa::PlatformWheelEventCocoa): Added. Encapsulate
and initialize data members specific to PLATFORM(COCOA).

(JSC::EncodingTraits<PlatformWheelEvent>::encodeValue): Added.
(JSC::EncodingTraits<PlatformWheelEvent>::decodeValue): Added.
* replay/SerializationMethods.h:
* replay/UserInputBridge.cpp: Fill in bridge methods to capture inputs.
(WebCore::UserInputBridge::handleWheelEvent):
(WebCore::UserInputBridge::scrollRecursively):
(WebCore::UserInputBridge::logicalScrollRecursively):
* replay/WebInputs.json: Add inputs and new enum types.

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

13 files changed:
ChangeLog
ManualTests/inspector/replay-wheel-events.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/scrolling/ScrollingCoordinator.cpp
Source/WebCore/page/scrolling/ScrollingCoordinator.h
Source/WebCore/platform/PlatformWheelEvent.h
Source/WebCore/platform/ScrollTypes.h
Source/WebCore/replay/ReplayController.cpp
Source/WebCore/replay/ReplayInputDispatchMethods.cpp
Source/WebCore/replay/SerializationMethods.cpp
Source/WebCore/replay/SerializationMethods.h
Source/WebCore/replay/UserInputBridge.cpp
Source/WebCore/replay/WebInputs.json

index 06157b607d9a367c8655ac85f4aa0dc254941113..d12ba14211eaa3b0de72acbe001f268084999c93 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2014-04-04  Brian J. Burg  <burg@cs.washington.edu>
+
+        Web Replay: capture and replay wheel events and scroll commands
+        https://bugs.webkit.org/show_bug.cgi?id=129402
+
+        Reviewed by Timothy Hatcher.
+
+        * ManualTests/inspector/replay-wheel-events.html: Added.
+
 2014-04-04  Raphael Kubo da Costa  <raphael.kubo.da.costa@intel.com>
 
         [GTK][CMake] Remove FindXt.cmake.
diff --git a/ManualTests/inspector/replay-wheel-events.html b/ManualTests/inspector/replay-wheel-events.html
new file mode 100644 (file)
index 0000000..638df0c
--- /dev/null
@@ -0,0 +1,86 @@
+<!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">
+
+    document.onmousewheel = handleEvent;
+    
+    window.dumpedEvents = [];
+    
+    function handleEvent(event) {
+        var properties = ["type", "eventPhase", "bubbles", "cancelable", "screenX", "screenY", "clientX", "clientY", "ctrlKey", "shiftKey", "altKey", "metaKey", "button", "deltaX", "deltaY", "deltaZ", "deltaMode", "wheelDeltaX", "wheelDeltaY"];
+        obj = {};
+        for (var key of properties)
+            obj[key] = event[key];
+    
+        dumpedEvents.push(obj);
+
+        var block = createBlock(hex_md5(JSON.stringify(obj)));
+        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: 10px;
+    height: 10px;
+    border-radius: 5px;
+    text-align: center;
+}
+</style>
+</head>
+<body>
+<p>This page is a manual test for capture and replay of scroll-related DOM events.</p>
+<p>Below, a block is created for each mousewheel event, where the color is derived from a hash of the event data. At the bottom is a cumulative hash of all event data.</p>
+<hr/>
+<textarea rows="3">
+
+This is a scrollable textarea.
+
+.
+
+..
+
+...
+
+....
+
+.....
+
+</textarea>
+<p>
+To test the replay functionality, open the Web Inspector, start capturing, and then scroll each of the iframe, overflow:scroll element, and the main page's flowed text. After some time, stop capturing and then replay.</p>
+<p>The replayed execution should produce the same sequence of blocks, and page contents should scroll the same distance and speed. 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>
index 9200a04f82b7cf6ae07cd1f43b6b40c9f9535e93..d3bcb3908185dcbe4b1a29996e93117f6ce5b879 100644 (file)
@@ -1,3 +1,63 @@
+2014-04-04  Brian J. Burg  <burg@cs.washington.edu>
+
+        Web Replay: capture and replay wheel events and scroll commands
+        https://bugs.webkit.org/show_bug.cgi?id=129402
+
+        Reviewed by Timothy Hatcher and Simon Fraser.
+
+        To capture and replay scrolling, the scrolling coordinator will force synchronous 
+        scrolling during capture and replay. If the page is capturing or replaying,
+        ForceOnMainThread will be added to the coordinator's SynchronousScrollingReasons.
+
+        A callback was added to signal that replay session state have changed,
+        and thus the synchronous scrolling reasons should be recomputed.
+
+        Automated replay reftests for scrolling are not included, because they will be
+        too flaky until more nondeterminism is handled. Specifically, resource loading,
+        initial focus/active state, and parsing are known blocking issues.
+
+        Test: ManualTests/inspector/replay-wheel-events.html
+
+        * page/scrolling/ScrollingCoordinator.cpp: Add a new callback for replay state
+        session changes. Add ForceOnMainThread if the page's active input cursor is
+        capturing or replaying.
+
+        (WebCore::ScrollingCoordinator::synchronousScrollingReasons):
+        (WebCore::ScrollingCoordinator::replaySessionStateDidChange): Added.
+        * page/scrolling/ScrollingCoordinator.h:
+
+        * platform/PlatformWheelEvent.h:
+        * platform/ScrollTypes.h: Add explicit enum storage types so these enums can
+        be forward-declared. This is necessary to generate enum encode/decode implementations.
+
+        * replay/ReplayController.cpp:
+        (WebCore::ReplayController::setForceDeterministicSettings): If async scrolling
+        support is available, tell the scrolling tree to behave deterministically.
+
+        * replay/ReplayInputDispatchMethods.cpp:
+        (WebCore::HandleWheelEvent::dispatch): Added.
+        (WebCore::LogicalScrollPage::dispatch): Added.
+        (WebCore::ScrollPage::dispatch): Added.
+        * replay/SerializationMethods.cpp: Introduce more specific macros for values
+        deserialized to arbitraray lvalues, scalars, RefPtr, and unique_ptr. Fix existing
+        uses of decode macros.
+
+        (JSC::EncodingTraits<NondeterministicInputBase>::encodeValue): Fix macro name.
+        (JSC::EncodingTraits<KeypressCommand>::encodeValue): Fix macro name.
+        (JSC::EncodingTraits<PlatformKeyboardEvent>::encodeValue): Fix macro name.
+        (JSC::EncodingTraits<PlatformMouseEvent>::encodeValue): Fix macro name.
+        (JSC::PlatformWheelEventCocoa::PlatformWheelEventCocoa): Added. Encapsulate
+        and initialize data members specific to PLATFORM(COCOA).
+
+        (JSC::EncodingTraits<PlatformWheelEvent>::encodeValue): Added.
+        (JSC::EncodingTraits<PlatformWheelEvent>::decodeValue): Added.
+        * replay/SerializationMethods.h:
+        * replay/UserInputBridge.cpp: Fill in bridge methods to capture inputs.
+        (WebCore::UserInputBridge::handleWheelEvent):
+        (WebCore::UserInputBridge::scrollRecursively):
+        (WebCore::UserInputBridge::logicalScrollRecursively):
+        * replay/WebInputs.json: Add inputs and new enum types.
+
 2014-04-04  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Upgrade to SelectorFailsAllSiblings when Child selector is failed.
index 8dad160047ffa4d1ad1aee2fa92263b19c5f2699..7701bc6612fc65b5d60a245b99e8f65c0097d498 100644 (file)
 #include "ScrollingCoordinatorCoordinatedGraphics.h"
 #endif
 
+#if ENABLE(WEB_REPLAY)
+#include "ReplayController.h"
+#include <replay/InputCursor.h>
+#endif
+
 namespace WebCore {
 
 #if !PLATFORM(COCOA)
@@ -299,6 +304,11 @@ SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons()
 
     if (m_forceSynchronousScrollLayerPositionUpdates)
         synchronousScrollingReasons |= ForcedOnMainThread;
+#if ENABLE(WEB_REPLAY)
+    InputCursor& cursor = m_page->replayController().activeInputCursor();
+    if (cursor.isCapturing() || cursor.isReplaying())
+        synchronousScrollingReasons |= ForcedOnMainThread;
+#endif
     if (frameView->hasSlowRepaintObjects())
         synchronousScrollingReasons |= HasSlowRepaintObjects;
     if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
@@ -325,6 +335,13 @@ void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool fo
     updateSynchronousScrollingReasons();
 }
 
+#if ENABLE(WEB_REPLAY)
+void ScrollingCoordinator::replaySessionStateDidChange()
+{
+    updateSynchronousScrollingReasons();
+}
+#endif
+
 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
 {
     static ScrollingNodeID uniqueScrollLayerID = 1;
index 9dcb8fb0294be50fdf35456f7be683804accb4c6..789c51449d49c7d8cf875ffe7d7580b41b456f86 100644 (file)
@@ -140,6 +140,11 @@ public:
     void handleWheelEventPhase(PlatformWheelEventPhase);
 #endif
 
+#if ENABLE(WEB_REPLAY)
+    // Called when the page transitions between executing normally and deterministically.
+    void replaySessionStateDidChange();
+#endif
+
     // Force all scroll layer position updates to happen on the main thread.
     void setForceSynchronousScrollLayerPositionUpdates(bool);
 
index aa308f8eee8929d6827875f87d421eb62831d455..62fdb661a069ff6061a6e65927e4e12cff2c2f21 100644 (file)
@@ -48,13 +48,13 @@ namespace WebCore {
     // and synthesized in other cases where platforms generate line-by-line scrolling events.
     // The ScrollByPageWheelEvent indicates that the wheel event should scroll an entire page.  In this case WebCore's built in paging behavior is used to page
     // up and down (you get the same behavior as if the user was clicking in a scrollbar track to page up or page down).
-    enum PlatformWheelEventGranularity {
+    enum PlatformWheelEventGranularity : uint64_t {
         ScrollByPageWheelEvent,
         ScrollByPixelWheelEvent,
     };
 
 #if PLATFORM(COCOA)
-    enum PlatformWheelEventPhase {
+    enum PlatformWheelEventPhase : uint64_t {
         PlatformWheelEventPhaseNone        = 0,
         PlatformWheelEventPhaseBegan       = 1 << 0,
         PlatformWheelEventPhaseStationary  = 1 << 1,
index c98d7785b3f126c05a3801ccbf829f676e8c5764..5fd35c922165cda7bdbfd73999a1c31b9f7416bd 100644 (file)
 
 namespace WebCore {
 
-    enum ScrollDirection {
+    enum ScrollDirection : uint64_t {
         ScrollUp,
         ScrollDown,
         ScrollLeft,
         ScrollRight
     };
 
-    enum ScrollLogicalDirection {
+    enum ScrollLogicalDirection : uint64_t {
         ScrollBlockDirectionBackward,
         ScrollBlockDirectionForward,
         ScrollInlineDirectionBackward,
@@ -103,7 +103,7 @@ namespace WebCore {
         return ScrollUp;
     }
 
-    enum ScrollGranularity {
+    enum ScrollGranularity : uint64_t {
         ScrollByLine,
         ScrollByPage,
         ScrollByDocument,
index edfc9eb0b46303d06e3e9b21b7a1cd0c361135ef..c907ba2600e18354cbcdc3197237ebdc05144159 100644 (file)
 #include <replay/EmptyInputCursor.h>
 #include <wtf/text/CString.h>
 
+#if ENABLE(ASYNC_SCROLLING)
+#include "ScrollingCoordinator.h"
+#endif
+
 namespace WebCore {
 
 ReplayController::ReplayController(Page& page)
@@ -66,17 +70,20 @@ ReplayController::ReplayController(Page& page)
 {
 }
 
-void ReplayController::setForceDeterministicSettings(bool shouldForce)
+void ReplayController::setForceDeterministicSettings(bool shouldForceDeterministicBehavior)
 {
-    ASSERT(shouldForce ^ (m_sessionState == SessionState::Inactive));
+    ASSERT(shouldForceDeterministicBehavior ^ (m_sessionState == SessionState::Inactive));
 
-    if (shouldForce) {
+    if (shouldForceDeterministicBehavior) {
         m_savedSettings.usesPageCache = m_page.settings().usesPageCache();
 
         m_page.settings().setUsesPageCache(false);
     } else {
         m_page.settings().setUsesPageCache(m_savedSettings.usesPageCache);
     }
+
+    if (ScrollingCoordinator* scrollingCoordinator = m_page.scrollingCoordinator())
+        scrollingCoordinator->replaySessionStateDidChange();
 }
 
 void ReplayController::setSessionState(SessionState state)
index 1045e65bf806c2ebdb6f0ac02923fa66017e673a..fba67dcc91622d76d7473e11a4d4ea64c94f3654 100644 (file)
@@ -77,6 +77,21 @@ void HandleMouseRelease::dispatch(ReplayController& controller)
     controller.page().userInputBridge().handleMouseReleaseEvent(platformEvent(), InputSource::Synthetic);
 }
 
+void HandleWheelEvent::dispatch(ReplayController& controller)
+{
+    controller.page().userInputBridge().handleWheelEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void LogicalScrollPage::dispatch(ReplayController& controller)
+{
+    controller.page().userInputBridge().logicalScrollRecursively(direction(), granularity(), InputSource::Synthetic);
+}
+
+void ScrollPage::dispatch(ReplayController& controller)
+{
+    controller.page().userInputBridge().scrollRecursively(direction(), granularity(), InputSource::Synthetic);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WEB_REPLAY)
index 5cb9ef16a3d06735970a296af343cf9e4748df6f..72645b35760a37e6611ef34317990803c456838f 100644 (file)
@@ -33,6 +33,7 @@
 #include "AllReplayInputs.h"
 #include "PlatformKeyboardEvent.h"
 #include "PlatformMouseEvent.h"
+#include "PlatformWheelEvent.h"
 #include "ReplayInputTypes.h"
 #include "SecurityOrigin.h"
 #include "URL.h"
@@ -44,31 +45,55 @@ using WebCore::MouseButton;
 using WebCore::PlatformEvent;
 using WebCore::PlatformKeyboardEvent;
 using WebCore::PlatformMouseEvent;
+using WebCore::PlatformWheelEvent;
+using WebCore::PlatformWheelEventGranularity;
 using WebCore::SecurityOrigin;
 using WebCore::URL;
 using WebCore::inputTypes;
 
+#if PLATFORM(COCOA)
+using WebCore::PlatformWheelEventPhase;
+#endif
+
 #define IMPORT_FROM_WEBCORE_NAMESPACE(name) \
 using WebCore::name; \
 
 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) \
+#define ENCODE_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)) \
+#define ENCODE_OPTIONAL_TYPE_WITH_KEY(_encodedValue, _type, _key, _value, condition) \
+    if (condition) \
+        ENCODE_TYPE_WITH_KEY(_encodedValue, _type, _key, _value)
+
+#define DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _lvalue) \
+    if (!_encodedValue.get<_type>(ASCIILiteral(#_key), _lvalue)) \
         return false
 
-#define ENCODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key, _value, condition) \
-    if (condition) \
-        _encodedValue.put<_type>(ASCIILiteral(#_key), _value)
+#define DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _lvalue) \
+    bool _key ## WasDecoded = _encodedValue.get<_type>(ASCIILiteral(#_key), _lvalue)
+
+#define DECODE_REFCOUNTED_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    RefPtr<_type> _key; \
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
+
+#define DECODE_UNIQUE_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    std::unique_ptr<_type> _key; \
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
+
+#define DECODE_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    _type _key; \
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
 
 #define DECODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(_encodedValue, _type, _key) \
     _type _key; \
-    bool _key ## WasDecoded = _encodedValue.get<_type>(ASCIILiteral(#_key), _key)
+    DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
+
+#define DECODE_OPTIONAL_REFCOUNTED_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+    RefPtr<_type> _key; \
+    DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
 
 namespace JSC {
 
@@ -77,7 +102,7 @@ EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const Nondet
     EncodedValue encodedValue = EncodedValue::createObject();
     const AtomicString& type = input.type();
 
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, type, type.string());
+    ENCODE_TYPE_WITH_KEY(encodedValue, String, type, type.string());
 
 #define ENCODE_IF_TYPE_TAG_MATCHES(name) \
     if (type == inputTypes().name) { \
@@ -134,8 +159,8 @@ EncodedValue EncodingTraits<KeypressCommand>::encodeValue(const KeypressCommand&
 {
     EncodedValue encodedValue = EncodedValue::createObject();
 
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, commandName, command.commandName);
-    ENCODE_OPTIONAL_SCALAR_TYPE_WITH_KEY(encodedValue, String, text, command.text, !command.text.isEmpty());
+    ENCODE_TYPE_WITH_KEY(encodedValue, String, commandName, command.commandName);
+    ENCODE_OPTIONAL_TYPE_WITH_KEY(encodedValue, String, text, command.text, !command.text.isEmpty());
 
     return encodedValue;
 }
@@ -164,21 +189,21 @@ EncodedValue EncodingTraits<PlatformKeyboardEvent>::encodeValue(const PlatformKe
 {
     EncodedValue encodedValue = EncodedValue::createObject();
 
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, double, timestamp, input.timestamp());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers, static_cast<PlatformEvent::Modifiers>(input.modifiers()));
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, text, input.text());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, unmodifiedText, input.unmodifiedText());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, String, keyIdentifier, input.keyIdentifier());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode, input.windowsVirtualKeyCode());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode, input.nativeVirtualKeyCode());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, int, macCharCode, input.macCharCode());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, autoRepeat, input.isAutoRepeat());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, keypad, input.isKeypad());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, systemKey, input.isSystemKey());
+    ENCODE_TYPE_WITH_KEY(encodedValue, double, timestamp, input.timestamp());
+    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
+    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers, static_cast<PlatformEvent::Modifiers>(input.modifiers()));
+    ENCODE_TYPE_WITH_KEY(encodedValue, String, text, input.text());
+    ENCODE_TYPE_WITH_KEY(encodedValue, String, unmodifiedText, input.unmodifiedText());
+    ENCODE_TYPE_WITH_KEY(encodedValue, String, keyIdentifier, input.keyIdentifier());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode, input.windowsVirtualKeyCode());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode, input.nativeVirtualKeyCode());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, macCharCode, input.macCharCode());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, autoRepeat, input.isAutoRepeat());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, keypad, input.isKeypad());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, systemKey, input.isSystemKey());
 #if USE(APPKIT)
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod, input.handledByInputMethod());
-    ENCODE_SCALAR_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands, input.commands());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod, input.handledByInputMethod());
+    ENCODE_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands, input.commands());
 #endif
     return encodedValue;
 }
@@ -215,18 +240,18 @@ EncodedValue EncodingTraits<PlatformMouseEvent>::encodeValue(const PlatformMouse
 {
     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());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, positionX, input.position().x());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, positionY, input.position().y());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, globalPositionX, input.globalPosition().x());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, globalPositionY, input.globalPosition().y());
+    ENCODE_TYPE_WITH_KEY(encodedValue, MouseButton, button, input.button());
+    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, clickCount, input.clickCount());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, shiftKey, input.shiftKey());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, ctrlKey, input.ctrlKey());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, altKey, input.altKey());
+    ENCODE_TYPE_WITH_KEY(encodedValue, bool, metaKey, input.metaKey());
+    ENCODE_TYPE_WITH_KEY(encodedValue, int, timestamp, input.timestamp());
 
     return encodedValue;
 }
@@ -253,6 +278,102 @@ bool EncodingTraits<PlatformMouseEvent>::decodeValue(EncodedValue& encodedValue,
     return true;
 }
 
+#if PLATFORM(COCOA)
+struct PlatformWheelEventCocoaArguments {
+    bool directionInvertedFromDevice;
+    bool hasPreciseScrollingDeltas;
+    PlatformWheelEventPhase phase;
+    PlatformWheelEventPhase momentumPhase;
+    int scrollCount;
+    float unacceleratedScrollingDeltaX;
+    float unacceleratedScrollingDeltaY;
+};
+
+class PlatformWheelEventCocoa : public PlatformWheelEvent {
+public:
+    PlatformWheelEventCocoa(PlatformWheelEvent& event, PlatformWheelEventCocoaArguments& arguments)
+        : PlatformWheelEvent(event)
+    {
+        m_directionInvertedFromDevice = arguments.directionInvertedFromDevice;
+        m_hasPreciseScrollingDeltas = arguments.hasPreciseScrollingDeltas;
+        m_phase = arguments.phase;
+        m_momentumPhase = arguments.momentumPhase;
+        m_scrollCount = arguments.scrollCount;
+        m_unacceleratedScrollingDeltaX = arguments.unacceleratedScrollingDeltaX;
+        m_unacceleratedScrollingDeltaY = arguments.unacceleratedScrollingDeltaY;
+    }
+};
+#endif // PLATFORM(COCOA)
+
+EncodedValue EncodingTraits<PlatformWheelEvent>::encodeValue(const PlatformWheelEvent& input)
+{
+    EncodedValue encodedData = EncodedValue::createObject();
+
+    ENCODE_TYPE_WITH_KEY(encodedData, int, positionX, input.position().x());
+    ENCODE_TYPE_WITH_KEY(encodedData, int, positionY, input.position().y());
+    ENCODE_TYPE_WITH_KEY(encodedData, int, globalPositionX, input.globalPosition().x());
+    ENCODE_TYPE_WITH_KEY(encodedData, int, globalPositionY, input.globalPosition().y());
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, shiftKey, input.shiftKey());
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, ctrlKey, input.ctrlKey());
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, altKey, input.altKey());
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, metaKey, input.metaKey());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, deltaX, input.deltaX());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, deltaY, input.deltaY());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, wheelTicksX, input.wheelTicksX());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, wheelTicksY, input.wheelTicksY());
+    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventGranularity, granularity, static_cast<PlatformWheelEventGranularity>(input.granularity()));
+
+#if PLATFORM(COCOA)
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, directionInvertedFromDevice, input.directionInvertedFromDevice());
+    ENCODE_TYPE_WITH_KEY(encodedData, bool, hasPreciseScrollingDeltas, input.hasPreciseScrollingDeltas());
+    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventPhase, phase, static_cast<PlatformWheelEventPhase>(input.phase()));
+    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventPhase, momentumPhase, static_cast<PlatformWheelEventPhase>(input.momentumPhase()));
+    ENCODE_TYPE_WITH_KEY(encodedData, int, scrollCount, input.scrollCount());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, unacceleratedScrollingDeltaX, input.unacceleratedScrollingDeltaX());
+    ENCODE_TYPE_WITH_KEY(encodedData, float, unacceleratedScrollingDeltaY, input.unacceleratedScrollingDeltaY());
+#endif
+
+    return encodedData;
+}
+
+bool EncodingTraits<PlatformWheelEvent>::decodeValue(EncodedValue& encodedData, std::unique_ptr<PlatformWheelEvent>& input)
+{
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, int, positionX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, int, positionY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, int, globalPositionX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, int, globalPositionY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, bool, shiftKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, bool, ctrlKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, bool, altKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, bool, metaKey);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, float, deltaX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, float, deltaY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, float, wheelTicksX);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, float, wheelTicksY);
+    DECODE_SCALAR_TYPE_WITH_KEY(encodedData, PlatformWheelEventGranularity, granularity);
+
+#if PLATFORM(COCOA)
+    PlatformWheelEventCocoaArguments arguments;
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, directionInvertedFromDevice, arguments.directionInvertedFromDevice);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, hasPreciseScrollingDeltas, arguments.hasPreciseScrollingDeltas);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PlatformWheelEventPhase, phase, arguments.phase);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PlatformWheelEventPhase, momentumPhase, arguments.momentumPhase);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, int, scrollCount, arguments.scrollCount);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, float, unacceleratedScrollingDeltaX, arguments.unacceleratedScrollingDeltaX);
+    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, float, unacceleratedScrollingDeltaY, arguments.unacceleratedScrollingDeltaY);
+#endif
+
+    PlatformWheelEvent event(IntPoint(positionX, positionY), IntPoint(globalPositionX, globalPositionY),
+        deltaX, deltaY, wheelTicksX, wheelTicksY, granularity, shiftKey, ctrlKey, altKey, metaKey);
+
+#if PLATFORM(COCOA)
+    input = std::make_unique<PlatformWheelEventCocoa>(event, arguments);
+#else
+    input = std::make_unique<PlatformWheelEvent>(event);
+#endif
+    return true;
+}
+
 EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input)
 {
     return EncodedValue::createString(input->toString());
index 7afa232b626da0da5044181abf52f9527d9705eb..37895e5a823d46d89eeac8532ac0dc2391a65fa0 100644 (file)
@@ -40,6 +40,7 @@ class Frame;
 class Page;
 class PlatformKeyboardEvent;
 class PlatformMouseEvent;
+class PlatformWheelEvent;
 class SecurityOrigin;
 class URL;
 
@@ -81,6 +82,13 @@ template<> struct EncodingTraits<WebCore::PlatformMouseEvent> {
     static bool decodeValue(EncodedValue&, std::unique_ptr<WebCore::PlatformMouseEvent>& value);
 };
 
+template<> struct EncodingTraits<WebCore::PlatformWheelEvent> {
+    typedef WebCore::PlatformWheelEvent DecodedType;
+
+    static EncodedValue encodeValue(const WebCore::PlatformWheelEvent& value);
+    static bool decodeValue(EncodedValue&, std::unique_ptr<WebCore::PlatformWheelEvent>& value);
+};
+
 template<> struct EncodingTraits<WebCore::URL> {
     typedef WebCore::URL DecodedType;
 
index 383c08c1cd623d48ab4e3e6ae10bbab2dea936db..fc8457a9f482962ddefaba025f6e34a894ace4f0 100644 (file)
@@ -160,8 +160,19 @@ bool UserInputBridge::handleAccessKeyEvent(const PlatformKeyboardEvent& keyEvent
     return m_page.focusController().focusedOrMainFrame().eventHandler().handleAccessKey(keyEvent);
 }
 
-bool UserInputBridge::handleWheelEvent(const PlatformWheelEvent& wheelEvent, InputSource)
+bool UserInputBridge::handleWheelEvent(const PlatformWheelEvent& wheelEvent, InputSource inputSource)
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing()) {
+        std::unique_ptr<PlatformWheelEvent> ownedEvent = std::make_unique<PlatformWheelEvent>(wheelEvent);
+        activeCursor().appendInput<HandleWheelEvent>(std::move(ownedEvent));
+    }
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.mainFrame().eventHandler().handleWheelEvent(wheelEvent);
 }
 
@@ -175,13 +186,31 @@ void UserInputBridge::focusSetFocused(bool focused, InputSource)
     m_page.focusController().setFocused(focused);
 }
 
-bool UserInputBridge::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, InputSource)
+bool UserInputBridge::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, InputSource inputSource)
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing())
+        activeCursor().appendInput<ScrollPage>(direction, granularity);
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.focusController().focusedOrMainFrame().eventHandler().scrollRecursively(direction, granularity, nullptr);
 }
 
-bool UserInputBridge::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, InputSource)
+bool UserInputBridge::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, InputSource inputSource)
 {
+#if ENABLE(WEB_REPLAY)
+    EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+    if (activeCursor().isCapturing())
+        activeCursor().appendInput<LogicalScrollPage>(direction, granularity);
+#else
+    UNUSED_PARAM(inputSource);
+#endif
+
     return m_page.focusController().focusedOrMainFrame().eventHandler().logicalScrollRecursively(direction, granularity, nullptr);
 }
 
index e24fd9ba4880c0235692e73a685419a4c69c619e..3e6aefb10f8efd2930fdc32c8e56002485df092c 100644 (file)
                 "name": "PlatformMouseEvent", "mode": "OWNED",
                 "header": "platform/PlatformMouseEvent.h"
             },
+            {
+                "name": "PlatformWheelEvent", "mode": "OWNED",
+                "header": "platform/PlatformWheelEvent.h"
+            },
+            {
+                "name": "PlatformWheelEventGranularity", "mode": "SCALAR", "storage": "uint64_t",
+                "flags": ["ENUM"],
+                "values": ["ScrollByPageWheelEvent", "ScrollByPixelWheelEvent"],
+                "header": "platform/PlatformWheelEvent.h"
+            },
+            {
+                "name": "PlatformWheelEventPhase", "mode": "SCALAR", "storage": "uint64_t",
+                "flags": ["ENUM"],
+                "guard": "PLATFORM(COCOA)",
+                "values": [
+                    "PlatformWheelEventPhaseNone",
+                    "PlatformWheelEventPhaseBegan",
+                    "PlatformWheelEventPhaseStationary",
+                    "PlatformWheelEventPhaseChanged",
+                    "PlatformWheelEventPhaseEnded",
+                    "PlatformWheelEventPhaseCancelled",
+                    "PlatformWheelEventPhaseMayBegin"
+                ],
+                "header": "platform/PlatformWheelEvent.h"
+            },
+            {
+                "name": "ScrollDirection", "mode": "SCALAR", "storage": "uint64_t",
+                "flags": ["ENUM"],
+                "values": ["ScrollUp", "ScrollDown", "ScrollLeft", "ScrollRight"],
+                "header": "platform/ScrollTypes.h"
+            },
+            {
+                "name": "ScrollGranularity", "mode": "SCALAR", "storage": "uint64_t",
+                "flags": ["ENUM"],
+                "values": [
+                    "ScrollByLine",
+                    "ScrollByPage",
+                    "ScrollByDocument",
+                    "ScrollByPixel",
+                    "ScrollByPrecisePixel"
+                ],
+                "header": "platform/ScrollTypes.h"
+            },
+            {
+                "name": "ScrollLogicalDirection", "mode": "SCALAR", "storage": "uint64_t",
+                "flags": ["ENUM"],
+                "values": [
+                    "ScrollBlockDirectionBackward",
+                    "ScrollBlockDirectionForward",
+                    "ScrollInlineDirectionBackward",
+                    "ScrollInlineDirectionForward"
+                ],
+                "header": "platform/ScrollTypes.h"
+            },
             {
                 "name": "SecurityOrigin", "mode": "SHARED",
                 "header": "page/SecurityOrigin.h"
                 { "name": "platformEvent", "type": "PlatformKeyboardEvent" }
             ]
         },
+        {
+            "name": "HandleWheelEvent",
+            "description": "The embedder signalled a mouse wheel event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "platformEvent", "type": "PlatformWheelEvent" }
+            ]
+        },
         {
             "name": "InitialNavigation",
             "description": "Initiate the initial main frame navigation.",
                 { "name": "url", "type": "URL" },
                 { "name": "referrer", "type": "String" }
             ]
+        },
+        {
+            "name": "LogicalScrollPage",
+            "description": "The embedder signalled a logical scroll event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "direction", "type": "ScrollLogicalDirection" },
+                { "name": "granularity", "type": "ScrollGranularity" }
+            ]
+        },
+        {
+            "name": "ScrollPage",
+            "description": "The embedder signalled a scroll event.",
+            "queue": "EVENT_LOOP",
+            "members": [
+                { "name": "direction", "type": "ScrollDirection" },
+                { "name": "granularity", "type": "ScrollGranularity" }
+            ]
         }
     ]
 }