Web Replay: detect possible replay divergence from unexpected DOM event dispatches
authorburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 7 Apr 2014 19:58:07 +0000 (19:58 +0000)
committerburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 7 Apr 2014 19:58:07 +0000 (19:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=131193

Reviewed by Andreas Kling.

Add assertions to catch potential nondeterministic behavior.

The assertion added by this patch catches dispatched DOM events
that are triggered by nondeterministic event loop cycles. If we
did not capture an event loop input in the current event loop
cycle nor are we manually simulating an event loop input during
playback, then DOM events fired during the unordered cycle could
run JavaScript and diverge the execution.

During playback, we can assert that EventLoopInputDispatcher is dispatching
when a DOM event is be dispatched to a document that is being replayed.

During capturing, event loop inputs are captured rather than
dispatched, so we add some accounting to track what caused a DOM
event. To approximate the extent of computation triggered by an
event loop input, we add RAII helpers to call sites where event
loop inputs are captured.

The assertions are disabled by default until the most common
sources of nondeterminism are handled and playback errors are
gracefully surfaced to the user. <https://webkit.org/b/131279>

No new tests. This patch adds extra assertions for debugging purposes.

* WebCore.xcodeproj/project.pbxproj:
* inspector/InspectorInstrumentation.cpp: Notify ReplayAgent of dispatched DOM event.
(WebCore::InspectorInstrumentation::willDispatchEventImpl):
(WebCore::InspectorInstrumentation::willDispatchEventOnWindowImpl):
* inspector/InspectorReplayAgent.cpp: Forward dispatched DOM events to ReplayController.
(WebCore::InspectorReplayAgent::willDispatchEvent): Added.
* inspector/InspectorReplayAgent.h:
* replay/CapturingInputCursor.cpp:
(WebCore::CapturingInputCursor::CapturingInputCursor):
(WebCore::CapturingInputCursor::setWithinEventLoopInputExtent): Added.
* replay/CapturingInputCursor.h:
* replay/EventLoopInput.cpp: Added.
(WebCore::EventLoopInputExtent::EventLoopInputExtent): Added.
(WebCore::EventLoopInputExtent::~EventLoopInputExtent): Added.
* replay/EventLoopInput.h:
* replay/EventLoopInputDispatcher.h:
(WebCore::EventLoopInputDispatcher::isDispatching): Add a getter.
* replay/ReplayController.cpp:
(WebCore::logDispatchedDOMEvent): Added. This is useful for understanding script-visible events.
(WebCore::ReplayController::willDispatchEvent): Added.
* replay/ReplayController.h:
* replay/UserInputBridge.cpp: Add extent helpers to call sites that capture inputs.
(WebCore::UserInputBridge::handleMousePressEvent):
(WebCore::UserInputBridge::handleMouseReleaseEvent):
(WebCore::UserInputBridge::handleMouseMoveEvent):
(WebCore::UserInputBridge::handleMouseMoveOnScrollbarEvent):
(WebCore::UserInputBridge::handleKeyEvent):
(WebCore::UserInputBridge::handleWheelEvent):
(WebCore::UserInputBridge::scrollRecursively):
(WebCore::UserInputBridge::logicalScrollRecursively):

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

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorReplayAgent.cpp
Source/WebCore/inspector/InspectorReplayAgent.h
Source/WebCore/replay/CapturingInputCursor.cpp
Source/WebCore/replay/CapturingInputCursor.h
Source/WebCore/replay/EventLoopInput.cpp [new file with mode: 0644]
Source/WebCore/replay/EventLoopInput.h
Source/WebCore/replay/EventLoopInputDispatcher.h
Source/WebCore/replay/ReplayController.cpp
Source/WebCore/replay/ReplayController.h
Source/WebCore/replay/UserInputBridge.cpp

index bc4fcc8..078e176 100644 (file)
@@ -1,3 +1,65 @@
+2014-04-07  Brian J. Burg  <burg@cs.washington.edu>
+
+        Web Replay: detect possible replay divergence from unexpected DOM event dispatches
+        https://bugs.webkit.org/show_bug.cgi?id=131193
+
+        Reviewed by Andreas Kling.
+
+        Add assertions to catch potential nondeterministic behavior.
+
+        The assertion added by this patch catches dispatched DOM events
+        that are triggered by nondeterministic event loop cycles. If we
+        did not capture an event loop input in the current event loop
+        cycle nor are we manually simulating an event loop input during
+        playback, then DOM events fired during the unordered cycle could
+        run JavaScript and diverge the execution.
+
+        During playback, we can assert that EventLoopInputDispatcher is dispatching
+        when a DOM event is be dispatched to a document that is being replayed.
+
+        During capturing, event loop inputs are captured rather than
+        dispatched, so we add some accounting to track what caused a DOM
+        event. To approximate the extent of computation triggered by an
+        event loop input, we add RAII helpers to call sites where event
+        loop inputs are captured.
+
+        The assertions are disabled by default until the most common
+        sources of nondeterminism are handled and playback errors are
+        gracefully surfaced to the user. <https://webkit.org/b/131279>
+
+        No new tests. This patch adds extra assertions for debugging purposes.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * inspector/InspectorInstrumentation.cpp: Notify ReplayAgent of dispatched DOM event.
+        (WebCore::InspectorInstrumentation::willDispatchEventImpl):
+        (WebCore::InspectorInstrumentation::willDispatchEventOnWindowImpl):
+        * inspector/InspectorReplayAgent.cpp: Forward dispatched DOM events to ReplayController.
+        (WebCore::InspectorReplayAgent::willDispatchEvent): Added.
+        * inspector/InspectorReplayAgent.h:
+        * replay/CapturingInputCursor.cpp:
+        (WebCore::CapturingInputCursor::CapturingInputCursor):
+        (WebCore::CapturingInputCursor::setWithinEventLoopInputExtent): Added.
+        * replay/CapturingInputCursor.h:
+        * replay/EventLoopInput.cpp: Added.
+        (WebCore::EventLoopInputExtent::EventLoopInputExtent): Added.
+        (WebCore::EventLoopInputExtent::~EventLoopInputExtent): Added.
+        * replay/EventLoopInput.h:
+        * replay/EventLoopInputDispatcher.h:
+        (WebCore::EventLoopInputDispatcher::isDispatching): Add a getter.
+        * replay/ReplayController.cpp:
+        (WebCore::logDispatchedDOMEvent): Added. This is useful for understanding script-visible events.
+        (WebCore::ReplayController::willDispatchEvent): Added.
+        * replay/ReplayController.h:
+        * replay/UserInputBridge.cpp: Add extent helpers to call sites that capture inputs.
+        (WebCore::UserInputBridge::handleMousePressEvent):
+        (WebCore::UserInputBridge::handleMouseReleaseEvent):
+        (WebCore::UserInputBridge::handleMouseMoveEvent):
+        (WebCore::UserInputBridge::handleMouseMoveOnScrollbarEvent):
+        (WebCore::UserInputBridge::handleKeyEvent):
+        (WebCore::UserInputBridge::handleWheelEvent):
+        (WebCore::UserInputBridge::scrollRecursively):
+        (WebCore::UserInputBridge::logicalScrollRecursively):
+
 2014-04-07  Timothy Hatcher  <timothy@apple.com>
 
         Remove copy of combine-javascript-resources.pl that isn't used anymore
index 8a2bb33..2d0c2e9 100644 (file)
                C3CF17A515B0063F00276D39 /* IdTargetObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C3CF17A115B0063F00276D39 /* IdTargetObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C3CF17A615B0063F00276D39 /* IdTargetObserverRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C3CF17A215B0063F00276D39 /* IdTargetObserverRegistry.cpp */; };
                C3CF17A715B0063F00276D39 /* IdTargetObserverRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = C3CF17A315B0063F00276D39 /* IdTargetObserverRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C400D10918F1C8F60090D863 /* EventLoopInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C400D10818F1C8F60090D863 /* EventLoopInput.cpp */; };
                C4CD629A18383766007EBAF1 /* FrameSnapshotting.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C4CD629818383766007EBAF1 /* FrameSnapshotting.cpp */; };
                C4CD629B18383766007EBAF1 /* FrameSnapshotting.h in Headers */ = {isa = PBXBuildFile; fileRef = C4CD629918383766007EBAF1 /* FrameSnapshotting.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C50B561612119D23008B46E0 /* GroupSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C50B561412119D23008B46E0 /* GroupSettings.cpp */; };
                C3CF17A215B0063F00276D39 /* IdTargetObserverRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IdTargetObserverRegistry.cpp; sourceTree = "<group>"; };
                C3CF17A315B0063F00276D39 /* IdTargetObserverRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IdTargetObserverRegistry.h; sourceTree = "<group>"; };
                C3E61C653A64807A83E76FB8 /* MathMLMencloseElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathMLMencloseElement.cpp; sourceTree = "<group>"; };
+               C400D10818F1C8F60090D863 /* EventLoopInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoopInput.cpp; sourceTree = "<group>"; };
                C4CD629818383766007EBAF1 /* FrameSnapshotting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FrameSnapshotting.cpp; sourceTree = "<group>"; };
                C4CD629918383766007EBAF1 /* FrameSnapshotting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FrameSnapshotting.h; sourceTree = "<group>"; };
                C50B561412119D23008B46E0 /* GroupSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GroupSettings.cpp; sourceTree = "<group>"; };
                                99CC0B3818BE9849006CEBCC /* AllReplayInputs.h */,
                                99CC0B3918BE9849006CEBCC /* CapturingInputCursor.cpp */,
                                99CC0B3A18BE9849006CEBCC /* CapturingInputCursor.h */,
+                               C400D10818F1C8F60090D863 /* EventLoopInput.cpp */,
                                99E45A1618A063BE0026D88F /* EventLoopInput.h */,
                                99CC0B3B18BE9849006CEBCC /* EventLoopInputDispatcher.cpp */,
                                99CC0B3C18BE9849006CEBCC /* EventLoopInputDispatcher.h */,
                                FD315FF812B0267600C1A359 /* AudioBuffer.cpp in Sources */,
                                FD315FFB12B0267600C1A359 /* AudioBufferSourceNode.cpp in Sources */,
                                FD31607B12B026F700C1A359 /* AudioBus.cpp in Sources */,
+                               C400D10918F1C8F60090D863 /* EventLoopInput.cpp in Sources */,
                                FD3160BB12B0272A00C1A359 /* AudioBusMac.mm in Sources */,
                                FD31607D12B026F700C1A359 /* AudioChannel.cpp in Sources */,
                                FD31600412B0267600C1A359 /* AudioContext.cpp in Sources */,
index ab4a4dd..ea6c59c 100644 (file)
@@ -350,6 +350,10 @@ InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventImpl(I
         timelineAgent->willDispatchEvent(event, document->frame());
         timelineAgentId = timelineAgent->id();
     }
+#if ENABLE(WEB_REPLAY)
+    if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent())
+        replayAgent->willDispatchEvent(event, document->frame());
+#endif
     return InspectorInstrumentationCookie(instrumentingAgents, timelineAgentId);
 }
 
@@ -378,6 +382,10 @@ InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventOnWind
         timelineAgent->willDispatchEvent(event, window ? window->frame() : nullptr);
         timelineAgentId = timelineAgent->id();
     }
+#if ENABLE(WEB_REPLAY)
+    if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent())
+        replayAgent->willDispatchEvent(event, window ? window->frame() : nullptr);
+#endif
     return InspectorInstrumentationCookie(instrumentingAgents, timelineAgentId);
 }
 
index 11cc727..912bea8 100644 (file)
@@ -198,6 +198,12 @@ void InspectorReplayAgent::frameDetached(Frame* frame)
         m_page.replayController().frameDetached(frame);
 }
 
+void InspectorReplayAgent::willDispatchEvent(const Event& event, Frame* frame)
+{
+    if (sessionState() != SessionState::Inactive)
+        m_page.replayController().willDispatchEvent(event, frame);
+}
+
 void InspectorReplayAgent::sessionCreated(PassRefPtr<ReplaySession> prpSession)
 {
     RefPtr<ReplaySession> session = prpSession;
index c6ce846..01ec1e9 100644 (file)
@@ -40,6 +40,7 @@
 namespace WebCore {
 
 class DocumentLoader;
+class Event;
 class Frame;
 class InspectorPageAgent;
 class InstrumentingAgents;
@@ -69,6 +70,7 @@ public:
     // Callbacks from InspectorInstrumentation.
     void frameNavigated(DocumentLoader*);
     void frameDetached(Frame*);
+    void willDispatchEvent(const Event&, Frame*);
 
     // Notifications from ReplayController.
     void sessionCreated(PassRefPtr<ReplaySession>);
index 253a5d1..c1897c7 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 
 CapturingInputCursor::CapturingInputCursor(SegmentedInputStorage& storage)
     : m_storage(storage)
+    , m_withinEventLoopInputExtent(false)
 {
     LOG(WebReplay, "%-30sCreated capture cursor=%p.\n", "[ReplayController]", this);
 }
@@ -71,6 +72,13 @@ NondeterministicInputBase* CapturingInputCursor::uncheckedLoadInput(InputQueue)
     return nullptr;
 }
 
+void CapturingInputCursor::setWithinEventLoopInputExtent(bool withinEventLoopInputExtent)
+{
+    // We cannot enter more than one extent at a time, since they represent a single run loop.
+    ASSERT(withinEventLoopInputExtent != m_withinEventLoopInputExtent);
+    m_withinEventLoopInputExtent = withinEventLoopInputExtent;
+}
+
 }; // namespace WebCore
 
 #endif // ENABLE(WEB_REPLAY)
index 8f0df4d..fc7289a 100644 (file)
@@ -35,6 +35,7 @@
 
 namespace WebCore {
 
+class EventLoopInputExtent;
 class SegmentedInputStorage;
 
 class CapturingInputCursor final : public InputCursor {
@@ -46,14 +47,19 @@ public:
     virtual bool isCapturing() const override { return true; }
     virtual bool isReplaying() const override { return false; }
 
+    void setWithinEventLoopInputExtent(bool);
+    bool withinEventLoopInputExtent() const { return m_withinEventLoopInputExtent; }
+
     virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
     virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
 protected:
     virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString& type) override;
+
 private:
     explicit CapturingInputCursor(SegmentedInputStorage&);
 
     SegmentedInputStorage& m_storage;
+    bool m_withinEventLoopInputExtent;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/replay/EventLoopInput.cpp b/Source/WebCore/replay/EventLoopInput.cpp
new file mode 100644 (file)
index 0000000..bb27026
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 University of Washington. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "EventLoopInput.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "CapturingInputCursor.h"
+
+namespace WebCore {
+
+EventLoopInputExtent::EventLoopInputExtent(InputCursor& cursor)
+    : m_cursor(cursor)
+{
+    if (!m_cursor.isCapturing())
+        return;
+
+    static_cast<CapturingInputCursor&>(cursor).setWithinEventLoopInputExtent(true);
+}
+
+EventLoopInputExtent::~EventLoopInputExtent()
+{
+    if (!m_cursor.isCapturing())
+        return;
+
+    static_cast<CapturingInputCursor&>(m_cursor).setWithinEventLoopInputExtent(false);
+}
+
+}; // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
index b4e3b70..0795d53 100644 (file)
 #include <replay/NondeterministicInput.h>
 #include <wtf/CurrentTime.h>
 
+namespace JSC {
+class InputCursor;
+};
+
 namespace WebCore {
 
 class ReplayController;
 
+// This is an RAII helper used during capturing which sets a flag on the input cursor
+// to track the dynamic extent of a captured event loop input. This extent approximates
+// the interval in which EventLoopInputDispatcher::dispatching() is true.
+class EventLoopInputExtent {
+    WTF_MAKE_NONCOPYABLE(EventLoopInputExtent);
+public:
+    EventLoopInputExtent(JSC::InputCursor&);
+    ~EventLoopInputExtent();
+private:
+    JSC::InputCursor& m_cursor;
+};
+
 class EventLoopInputBase : public NondeterministicInputBase {
 public:
     EventLoopInputBase()
index 61c013f..d641361 100644 (file)
@@ -68,6 +68,7 @@ public:
     DispatchSpeed dispatchSpeed() const { return m_speed; }
 
     bool isRunning() const { return m_running; }
+    bool isDispatching() const { return m_dispatching; }
 private:
     void dispatchInputSoon();
     void dispatchInput();
index c907ba2..8d6a34e 100644 (file)
@@ -37,6 +37,7 @@
 #include "Frame.h"
 #include "FrameTree.h"
 #include "InspectorInstrumentation.h"
+#include "Location.h"
 #include "Logging.h"
 #include "MainFrame.h"
 #include "Page.h"
@@ -44,6 +45,7 @@
 #include "ReplaySessionSegment.h"
 #include "ReplayingInputCursor.h"
 #include "ScriptController.h"
+#include "SerializationMethods.h"
 #include "Settings.h"
 #include "UserInputBridge.h"
 #include "WebReplayInputs.h"
 
 namespace WebCore {
 
+static void logDispatchedDOMEvent(const Event& event, bool eventIsUnrelated)
+{
+#if !LOG_DISABLED
+    EventTarget* target = event.target();
+    if (!target)
+        return;
+
+    // A DOM event is unrelated if it is being dispatched to a document that is neither capturing nor replaying.
+    if (Node* node = target->toNode()) {
+        LOG(WebReplay, "%-20s --->%s DOM event: type=%s, target=%lu/node[%p] %s\n", "ReplayEvents",
+            (eventIsUnrelated) ? "Unrelated" : "Dispatching",
+            event.type().string().utf8().data(),
+            frameIndexFromDocument((node->inDocument()) ? &node->document() : node->ownerDocument()),
+            node,
+            node->nodeName().utf8().data());
+    } else if (DOMWindow* window = target->toDOMWindow()) {
+        LOG(WebReplay, "%-20s --->%s DOM event: type=%s, target=%lu/window[%p] %s\n", "ReplayEvents",
+            (eventIsUnrelated) ? "Unrelated" : "Dispatching",
+            event.type().string().utf8().data(),
+            frameIndexFromDocument(window->document()),
+            window,
+            window->location()->href().utf8().data());
+    }
+#else
+    UNUSED_PARAM(event);
+    UNUSED_PARAM(eventIsUnrelated);
+#endif
+}
+
 ReplayController::ReplayController(Page& page)
     : m_page(page)
     , m_loadedSegment(nullptr)
@@ -360,6 +391,36 @@ void ReplayController::frameDetached(Frame* frame)
     // input has been dispatched. So, nothing needs to be done here.
 }
 
+void ReplayController::willDispatchEvent(const Event& event, Frame* frame)
+{
+    EventTarget* target = event.target();
+    if (!target && !frame)
+        return;
+
+    Document* document = frame ? frame->document() : nullptr;
+    // Fetch the document from the event target, because the target could be detached.
+    if (Node* node = target->toNode())
+        document = node->inDocument() ? &node->document() : node->ownerDocument();
+    else if (DOMWindow* window = target->toDOMWindow())
+        document = window->document();
+
+    ASSERT(document);
+
+    InputCursor& cursor = document->inputCursor();
+    bool eventIsUnrelated = !cursor.isCapturing() && !cursor.isReplaying();
+    logDispatchedDOMEvent(event, eventIsUnrelated);
+
+#if ENABLE_AGGRESSIVE_DETERMINISM_CHECKS
+    // To ensure deterministic JS execution, all DOM events must be dispatched deterministically.
+    // If these assertions fail, then this DOM event is being dispatched by a nondeterministic EventLoop
+    // cycle, and may cause program execution to diverge if any JS code runs because of the DOM event.
+    if (cursor.isCapturing())
+        ASSERT(static_cast<CapturingInputCursor&>(cursor).withinEventLoopInputExtent());
+    else if (cursor.isReplaying())
+        ASSERT(dispatcher().isDispatching());
+#endif
+}
+
 PassRefPtr<ReplaySession> ReplayController::loadedSession() const
 {
     return m_loadedSession;
index ef05771..077cc82 100644 (file)
 #include <wtf/Noncopyable.h>
 #include <wtf/Vector.h>
 
+// Determinism assertions are guarded by this macro. When a user-facing error reporting and
+// recovery mechanism is implemented, this guard can be removed. <https://webkit.org/b/131279>
+#define ENABLE_AGGRESSIVE_DETERMINISM_CHECKS 0
+
 namespace JSC {
 class InputCursor;
 }
@@ -129,6 +133,7 @@ public:
     // InspectorReplayAgent notifications.
     void frameNavigated(DocumentLoader*);
     void frameDetached(Frame*);
+    void willDispatchEvent(const Event&, Frame*);
 
     Page& page() const { return m_page; }
     SessionState sessionState() const { return m_sessionState; }
index fc8457a..364a3f0 100644 (file)
@@ -80,10 +80,12 @@ bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent& mouseEvent
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
-        activeCursor().appendInput<HandleMousePress>(std::move(ownedEvent));
+        cursor.appendInput<HandleMousePress>(std::move(ownedEvent));
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -96,10 +98,12 @@ bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEve
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
-        activeCursor().appendInput<HandleMouseRelease>(std::move(ownedEvent));
+        cursor.appendInput<HandleMouseRelease>(std::move(ownedEvent));
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -112,10 +116,12 @@ bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent,
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
-        activeCursor().appendInput<HandleMouseMove>(std::move(ownedEvent), false);
+        cursor.appendInput<HandleMouseMove>(std::move(ownedEvent), false);
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -128,10 +134,12 @@ bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent&
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
-        activeCursor().appendInput<HandleMouseMove>(std::move(ownedEvent), true);
+        cursor.appendInput<HandleMouseMove>(std::move(ownedEvent), true);
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -144,10 +152,12 @@ bool UserInputBridge::handleKeyEvent(const PlatformKeyboardEvent& keyEvent, Inpu
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformKeyboardEvent> ownedEvent = std::make_unique<PlatformKeyboardEvent>(keyEvent);
-        activeCursor().appendInput<HandleKeyPress>(std::move(ownedEvent));
+        cursor.appendInput<HandleKeyPress>(std::move(ownedEvent));
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -165,10 +175,12 @@ bool UserInputBridge::handleWheelEvent(const PlatformWheelEvent& wheelEvent, Inp
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing()) {
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing()) {
         std::unique_ptr<PlatformWheelEvent> ownedEvent = std::make_unique<PlatformWheelEvent>(wheelEvent);
-        activeCursor().appendInput<HandleWheelEvent>(std::move(ownedEvent));
+        cursor.appendInput<HandleWheelEvent>(std::move(ownedEvent));
     }
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -191,8 +203,11 @@ bool UserInputBridge::scrollRecursively(ScrollDirection direction, ScrollGranula
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing())
-        activeCursor().appendInput<ScrollPage>(direction, granularity);
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing())
+        cursor.appendInput<ScrollPage>(direction, granularity);
+
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif
@@ -205,8 +220,11 @@ bool UserInputBridge::logicalScrollRecursively(ScrollLogicalDirection direction,
 #if ENABLE(WEB_REPLAY)
     EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
 
-    if (activeCursor().isCapturing())
-        activeCursor().appendInput<LogicalScrollPage>(direction, granularity);
+    InputCursor& cursor = activeCursor();
+    if (cursor.isCapturing())
+        cursor.appendInput<LogicalScrollPage>(direction, granularity);
+
+    EventLoopInputExtent extent(cursor);
 #else
     UNUSED_PARAM(inputSource);
 #endif