Web Replay: dispatch timing information should be stored out-of-line in a replay...
authorburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Aug 2014 21:53:17 +0000 (21:53 +0000)
committerburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Aug 2014 21:53:17 +0000 (21:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=135295

Reviewed by Timothy Hatcher.

We need to save a timestamp for each event loop input so that replay can
simulate the original user and network delays. Currently that timestamp
is stored on each EventLoopInput instance.

This patch stores timestamp data in a separate vector attached to the segment.
The event loop input class is now immutable, and new auxiliary data can be added
without adding members to the EventLoopInput class.

As part of the refactoring, InputCursors now keep a reference to the relevant
session segment instead of a reference to their input storage. InputCursors can
be created directly, instead of through ReplaySessionSegment.

No new tests. No behavior was changed.

* inspector/InspectorReplayAgent.cpp:
(WebCore::buildInspectorObjectForInput): Don't send the timestamp with the input.
(WebCore::buildInspectorObjectForSegment):
* inspector/protocol/Replay.json: Remove optional timestamp field for ReplayInput.
* replay/CapturingInputCursor.cpp:
(WebCore::CapturingInputCursor::CapturingInputCursor):
(WebCore::CapturingInputCursor::create):
(WebCore::CapturingInputCursor::storeInput): Save event loop input timings here.
* replay/CapturingInputCursor.h:
* replay/EventLoopInput.h:
(WebCore::EventLoopInputBase::EventLoopInputBase): Deleted.
(WebCore::EventLoopInputBase::timestamp): Deleted.
(WebCore::EventLoopInputBase::setTimestamp): Deleted.
* replay/EventLoopInputDispatcher.cpp: Use a struct for dispatch information.
(WebCore::EventLoopInputDispatcher::EventLoopInputDispatcher):
(WebCore::EventLoopInputDispatcher::dispatchInputSoon):
(WebCore::EventLoopInputDispatcher::dispatchInput):
* replay/EventLoopInputDispatcher.h:
* replay/FunctorInputCursor.h:
(WebCore::FunctorInputCursor::forEachInputInQueue):
(WebCore::FunctorInputCursor::FunctorInputCursor):
* replay/ReplayController.cpp:
(WebCore::ReplayController::createSegment):
(WebCore::ReplayController::loadSegmentAtIndex):
(WebCore::ReplayController::unloadSegment): Deleted.
(WebCore::ReplayController::startPlayback): Deleted.
* replay/ReplaySessionSegment.cpp:
(WebCore::ReplaySessionSegment::createCapturingCursor): Deleted.
(WebCore::ReplaySessionSegment::createReplayingCursor): Deleted.
(WebCore::ReplaySessionSegment::createFunctorCursor): Deleted.
* replay/ReplaySessionSegment.h:
(WebCore::ReplaySessionSegment::storage):
(WebCore::ReplaySessionSegment::eventLoopTimings):
* replay/ReplayingInputCursor.cpp:
(WebCore::ReplayingInputCursor::ReplayingInputCursor):
(WebCore::ReplayingInputCursor::create):
(WebCore::ReplayingInputCursor::uncheckedLoadInput):
(WebCore::ReplayingInputCursor::loadEventLoopInput): Added. This method collates
and returns the next event loop input with its associated dispatch information.
* replay/ReplayingInputCursor.h:

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

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorReplayAgent.cpp
Source/WebCore/inspector/protocol/Replay.json
Source/WebCore/replay/CapturingInputCursor.cpp
Source/WebCore/replay/CapturingInputCursor.h
Source/WebCore/replay/EventLoopInput.h
Source/WebCore/replay/EventLoopInputDispatcher.cpp
Source/WebCore/replay/EventLoopInputDispatcher.h
Source/WebCore/replay/FunctorInputCursor.h
Source/WebCore/replay/ReplayController.cpp
Source/WebCore/replay/ReplaySessionSegment.cpp
Source/WebCore/replay/ReplaySessionSegment.h
Source/WebCore/replay/ReplayingInputCursor.cpp
Source/WebCore/replay/ReplayingInputCursor.h

index 7709424..07b1981 100644 (file)
@@ -1,3 +1,65 @@
+2014-08-06  Brian J. Burg  <burg@cs.washington.edu>
+
+        Web Replay: dispatch timing information should be stored out-of-line in a replay segment
+        https://bugs.webkit.org/show_bug.cgi?id=135295
+
+        Reviewed by Timothy Hatcher.
+
+        We need to save a timestamp for each event loop input so that replay can
+        simulate the original user and network delays. Currently that timestamp
+        is stored on each EventLoopInput instance.
+
+        This patch stores timestamp data in a separate vector attached to the segment.
+        The event loop input class is now immutable, and new auxiliary data can be added
+        without adding members to the EventLoopInput class.
+
+        As part of the refactoring, InputCursors now keep a reference to the relevant
+        session segment instead of a reference to their input storage. InputCursors can
+        be created directly, instead of through ReplaySessionSegment.
+
+        No new tests. No behavior was changed.
+
+        * inspector/InspectorReplayAgent.cpp:
+        (WebCore::buildInspectorObjectForInput): Don't send the timestamp with the input.
+        (WebCore::buildInspectorObjectForSegment):
+        * inspector/protocol/Replay.json: Remove optional timestamp field for ReplayInput.
+        * replay/CapturingInputCursor.cpp:
+        (WebCore::CapturingInputCursor::CapturingInputCursor):
+        (WebCore::CapturingInputCursor::create):
+        (WebCore::CapturingInputCursor::storeInput): Save event loop input timings here.
+        * replay/CapturingInputCursor.h:
+        * replay/EventLoopInput.h:
+        (WebCore::EventLoopInputBase::EventLoopInputBase): Deleted.
+        (WebCore::EventLoopInputBase::timestamp): Deleted.
+        (WebCore::EventLoopInputBase::setTimestamp): Deleted.
+        * replay/EventLoopInputDispatcher.cpp: Use a struct for dispatch information.
+        (WebCore::EventLoopInputDispatcher::EventLoopInputDispatcher):
+        (WebCore::EventLoopInputDispatcher::dispatchInputSoon):
+        (WebCore::EventLoopInputDispatcher::dispatchInput):
+        * replay/EventLoopInputDispatcher.h:
+        * replay/FunctorInputCursor.h:
+        (WebCore::FunctorInputCursor::forEachInputInQueue):
+        (WebCore::FunctorInputCursor::FunctorInputCursor):
+        * replay/ReplayController.cpp:
+        (WebCore::ReplayController::createSegment):
+        (WebCore::ReplayController::loadSegmentAtIndex):
+        (WebCore::ReplayController::unloadSegment): Deleted.
+        (WebCore::ReplayController::startPlayback): Deleted.
+        * replay/ReplaySessionSegment.cpp:
+        (WebCore::ReplaySessionSegment::createCapturingCursor): Deleted.
+        (WebCore::ReplaySessionSegment::createReplayingCursor): Deleted.
+        (WebCore::ReplaySessionSegment::createFunctorCursor): Deleted.
+        * replay/ReplaySessionSegment.h:
+        (WebCore::ReplaySessionSegment::storage):
+        (WebCore::ReplaySessionSegment::eventLoopTimings):
+        * replay/ReplayingInputCursor.cpp:
+        (WebCore::ReplayingInputCursor::ReplayingInputCursor):
+        (WebCore::ReplayingInputCursor::create):
+        (WebCore::ReplayingInputCursor::uncheckedLoadInput):
+        (WebCore::ReplayingInputCursor::loadEventLoopInput): Added. This method collates
+        and returns the next event loop input with its associated dispatch information.
+        * replay/ReplayingInputCursor.h:
+
 2014-08-06  Tim Horton  <timothy_horton@apple.com>
 
         Document-relative overlays disappear after doing page-cache navigations
index 41d985a..df0818e 100644 (file)
@@ -71,9 +71,6 @@ static PassRefPtr<TypeBuilder::Replay::ReplayInput> buildInspectorObjectForInput
         .setOffset(offset)
         .setData(encodedInput.asObject());
 
-    if (input.queue() == InputQueue::EventLoopInput)
-        inputObject->setTimestamp(static_cast<const EventLoopInputBase&>(input).timestamp());
-
     return inputObject.release();
 }
 
@@ -141,7 +138,7 @@ static PassRefPtr<TypeBuilder::Replay::SessionSegment> buildInspectorObjectForSe
     for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++) {
         SerializeInputToJSONFunctor collector;
         InputQueue queue = static_cast<InputQueue>(i);
-        RefPtr<FunctorInputCursor> functorCursor = segment->createFunctorCursor();
+        RefPtr<FunctorInputCursor> functorCursor = FunctorInputCursor::create(segment);
         RefPtr<TypeBuilder::Array<TypeBuilder::Replay::ReplayInput>> queueInputs = functorCursor->forEachInputInQueue(queue, collector);
 
         RefPtr<TypeBuilder::Replay::ReplayInputQueue> queueObject = TypeBuilder::Replay::ReplayInputQueue::create()
index 6a77726..be26988 100644 (file)
@@ -34,7 +34,6 @@
             "properties": [
                 { "name": "type", "type": "string", "description": "Input type." },
                 { "name": "offset", "type": "integer", "description": "Offset of this input in its respective queue."},
-                { "name": "timestamp", "type": "number", "optional": true, "description": "The timestamp of this input." },
                 { "name": "data", "type": "object", "description": "Per-input payload." }
             ]
         },
index daa06bc..40db3b6 100644 (file)
 
 #if ENABLE(WEB_REPLAY)
 
+#include "EventLoopInput.h"
 #include "Logging.h"
+#include "ReplaySessionSegment.h"
 #include "SegmentedInputStorage.h"
+#include <wtf/CurrentTime.h>
 
 namespace WebCore {
 
-CapturingInputCursor::CapturingInputCursor(SegmentedInputStorage& storage)
-    : m_storage(storage)
+CapturingInputCursor::CapturingInputCursor(PassRefPtr<ReplaySessionSegment> segment)
+    : m_segment(segment)
     , m_withinEventLoopInputExtent(false)
 {
     LOG(WebReplay, "%-30sCreated capture cursor=%p.\n", "[ReplayController]", this);
@@ -47,15 +50,22 @@ CapturingInputCursor::~CapturingInputCursor()
     LOG(WebReplay, "%-30sDestroyed capture cursor=%p.\n", "[ReplayController]", this);
 }
 
-PassRefPtr<CapturingInputCursor> CapturingInputCursor::create(SegmentedInputStorage& storage)
+PassRefPtr<CapturingInputCursor> CapturingInputCursor::create(PassRefPtr<ReplaySessionSegment> segment)
 {
-    return adoptRef(new CapturingInputCursor(storage));
+    return adoptRef(new CapturingInputCursor(segment));
 }
 
 void CapturingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase> input)
 {
-    ASSERT(input);
-    m_storage.store(WTF::move(input));
+    ASSERT_ARG(input, input);
+
+    if (input->queue() == InputQueue::EventLoopInput) {
+        // FIXME: rewrite this (and related dispatch code) to use std::chrono.
+        double now = monotonicallyIncreasingTime();
+        m_segment->eventLoopTimings().append(now);
+    }
+
+    m_segment->storage().store(WTF::move(input));
 }
 
 NondeterministicInputBase* CapturingInputCursor::loadInput(InputQueue, const AtomicString&)
index fc7289a..59e6d4c 100644 (file)
 namespace WebCore {
 
 class EventLoopInputExtent;
-class SegmentedInputStorage;
+class Page;
+class ReplaySessionSegment;
 
 class CapturingInputCursor final : public InputCursor {
     WTF_MAKE_NONCOPYABLE(CapturingInputCursor);
 public:
-    static PassRefPtr<CapturingInputCursor> create(SegmentedInputStorage&);
+    static PassRefPtr<CapturingInputCursor> create(PassRefPtr<ReplaySessionSegment>);
     virtual ~CapturingInputCursor();
 
     virtual bool isCapturing() const override { return true; }
@@ -50,15 +51,16 @@ public:
     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&);
+    CapturingInputCursor(PassRefPtr<ReplaySessionSegment>);
+
+    virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+    virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
 
-    SegmentedInputStorage& m_storage;
+    RefPtr<ReplaySessionSegment> m_segment;
     bool m_withinEventLoopInputExtent;
 };
 
index 0795d53..250a3db 100644 (file)
@@ -55,20 +55,10 @@ private:
 
 class EventLoopInputBase : public NondeterministicInputBase {
 public:
-    EventLoopInputBase()
-        : m_timestamp(monotonicallyIncreasingTime())
-    {
-    }
-
     virtual ~EventLoopInputBase() { }
     virtual InputQueue queue() const override final { return InputQueue::EventLoopInput; }
 
     virtual void dispatch(ReplayController&) = 0;
-
-    double timestamp() const { return m_timestamp; }
-    void setTimestamp(double timestamp) { m_timestamp = timestamp; }
-protected:
-    double m_timestamp;
 };
 
 template <typename InputType>
index 8502197..7f23a15 100644 (file)
@@ -49,13 +49,14 @@ EventLoopInputDispatcher::EventLoopInputDispatcher(Page& page, ReplayingInputCur
     , m_client(client)
     , m_cursor(cursor)
     , m_timer(this, &EventLoopInputDispatcher::timerFired)
-    , m_runningInput(nullptr)
     , m_dispatching(false)
     , m_running(false)
     , m_speed(DispatchSpeed::FastForward)
     , m_previousDispatchStartTime(0.0)
     , m_previousInputTimestamp(0.0)
 {
+    m_currentWork.input = nullptr;
+    m_currentWork.timestamp = 0.0;
 }
 
 void EventLoopInputDispatcher::run()
@@ -88,8 +89,8 @@ void EventLoopInputDispatcher::dispatchInputSoon()
     ASSERT(m_running);
 
     // We may already have an input if replay was paused just before dispatching.
-    if (!m_runningInput)
-        m_runningInput = static_cast<EventLoopInputBase*>(m_cursor.uncheckedLoadInput(InputQueue::EventLoopInput));
+    if (!m_currentWork.input)
+        m_currentWork = m_cursor.loadEventLoopInput();
 
     if (m_timer.isActive())
         m_timer.stop();
@@ -103,9 +104,9 @@ void EventLoopInputDispatcher::dispatchInputSoon()
         // observed delay between the previous and current input.
 
         if (!m_previousInputTimestamp)
-            m_previousInputTimestamp = m_runningInput->timestamp();
+            m_previousInputTimestamp = m_currentWork.timestamp;
 
-        double targetInterval = m_runningInput->timestamp() - m_previousInputTimestamp;
+        double targetInterval = m_currentWork.timestamp - m_previousInputTimestamp;
         double elapsed = monotonicallyIncreasingTime() - m_previousDispatchStartTime;
         waitInterval = targetInterval - elapsed;
     }
@@ -126,34 +127,34 @@ void EventLoopInputDispatcher::dispatchInputSoon()
 
 void EventLoopInputDispatcher::dispatchInput()
 {
-    ASSERT(m_runningInput);
+    ASSERT(m_currentWork.input);
     ASSERT(!m_dispatching);
 
     if (m_speed == DispatchSpeed::RealTime) {
         m_previousDispatchStartTime = monotonicallyIncreasingTime();
-        m_previousInputTimestamp = m_runningInput->timestamp();
+        m_previousInputTimestamp = m_currentWork.timestamp;
     }
 
 #if !LOG_DISABLED
-    EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(*m_runningInput);
+    EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(*m_currentWork.input);
     String jsonString = encodedInput.asObject()->toJSONString();
 
     LOG(WebReplay, "%-20s ----------------------------------------------", "ReplayEvents");
-    LOG(WebReplay, "%-20s >DISPATCH: %s %s\n", "ReplayEvents", m_runningInput->type().string().utf8().data(), jsonString.utf8().data());
+    LOG(WebReplay, "%-20s >DISPATCH: %s %s\n", "ReplayEvents", m_currentWork.input->type().string().utf8().data(), jsonString.utf8().data());
 #endif
 
-    m_client->willDispatchInput(*m_runningInput);
+    m_client->willDispatchInput(*m_currentWork.input);
     // Client could stop replay in the previous callback, so check again.
     if (!m_running)
         return;
 
     {
         TemporaryChange<bool> change(m_dispatching, true);
-        m_runningInput->dispatch(m_page.replayController());
+        m_currentWork.input->dispatch(m_page.replayController());
     }
 
-    EventLoopInputBase* dispatchedInput = m_runningInput;
-    m_runningInput = nullptr;
+    EventLoopInputBase* dispatchedInput = m_currentWork.input;
+    m_currentWork.input = nullptr;
 
     // Notify clients that the event was dispatched.
     m_client->didDispatchInput(*dispatchedInput);
index d641361..f43062e 100644 (file)
@@ -31,6 +31,7 @@
 #if ENABLE(WEB_REPLAY)
 
 #include "EventLoopInput.h"
+#include "ReplayingInputCursor.h"
 #include "Timer.h"
 #include <wtf/Noncopyable.h>
 #include <wtf/Vector.h>
@@ -39,7 +40,6 @@
 namespace WebCore {
 
 class Page;
-class ReplayingInputCursor;
 
 enum class DispatchSpeed {
     RealTime,
@@ -79,8 +79,8 @@ private:
     ReplayingInputCursor& m_cursor;
     Timer<EventLoopInputDispatcher> m_timer;
 
-    // This pointer is valid when an event loop input is presently dispatching.
-    EventLoopInputBase* m_runningInput;
+    // This data is valid when an event loop input is presently dispatching.
+    EventLoopInputData m_currentWork;
     // Whether the dispatcher is currently calling out to an inputs' dispatch() method.
     bool m_dispatching;
     // Whether the dispatcher is waiting to dispatch or actively dispatching inputs.
index d07821d..c8df903 100644 (file)
@@ -30,6 +30,7 @@
 
 #if ENABLE(WEB_REPLAY)
 
+#include "ReplaySessionSegment.h"
 #include "SegmentedInputStorage.h"
 #include <replay/InputCursor.h>
 #include <replay/NondeterministicInput.h>
@@ -42,9 +43,9 @@ namespace WebCore {
 class FunctorInputCursor final : public InputCursor {
     WTF_MAKE_NONCOPYABLE(FunctorInputCursor);
 public:
-    static PassRefPtr<FunctorInputCursor> create(SegmentedInputStorage& storage)
+    static PassRefPtr<FunctorInputCursor> create(PassRefPtr<ReplaySessionSegment> segment)
     {
-        return adoptRef(new FunctorInputCursor(storage));
+        return adoptRef(new FunctorInputCursor(segment));
     }
 
     // InputCursor
@@ -59,22 +60,22 @@ public:
 protected:
     virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString&) override;
 private:
-    FunctorInputCursor(SegmentedInputStorage&);
+    FunctorInputCursor(PassRefPtr<ReplaySessionSegment>);
 
-    SegmentedInputStorage& m_storage;
+    RefPtr<ReplaySessionSegment> m_segment;
 };
 
 template<typename Functor> inline
 typename Functor::ReturnType FunctorInputCursor::forEachInputInQueue(InputQueue queue, Functor& functor)
 {
-    for (size_t i = 0; i < m_storage.queueSize(queue); i++)
-        functor(i, m_storage.queue(queue).at(i).get());
+    for (size_t i = 0; i < m_segment->storage().queueSize(queue); i++)
+        functor(i, m_segment->storage().queue(queue).at(i).get());
 
     return functor.returnValue();
 }
 
-inline FunctorInputCursor::FunctorInputCursor(SegmentedInputStorage& storage)
-    : m_storage(storage)
+inline FunctorInputCursor::FunctorInputCursor(PassRefPtr<ReplaySessionSegment> segment)
+    : m_segment(segment)
 {
 }
 
index 83135d2..119f490 100644 (file)
@@ -225,7 +225,7 @@ void ReplayController::createSegment()
     LOG(WebReplay, "%-20s Created segment: %p.\n", "ReplayController", m_loadedSegment.get());
     InspectorInstrumentation::segmentCreated(&m_page, m_loadedSegment);
 
-    m_activeCursor = m_loadedSegment->createCapturingCursor(m_page);
+    m_activeCursor = CapturingInputCursor::create(m_loadedSegment);
     m_activeCursor->appendInput<BeginSegmentSentinel>();
 
     std::unique_ptr<InitialNavigation> navigationInput = InitialNavigation::createFromPage(m_page);
@@ -269,7 +269,7 @@ void ReplayController::loadSegmentAtIndex(size_t segmentIndex)
     m_currentPosition.segmentOffset = segmentIndex;
     m_currentPosition.inputOffset = 0;
 
-    m_activeCursor = m_loadedSegment->createReplayingCursor(m_page, this);
+    m_activeCursor = ReplayingInputCursor::create(m_loadedSegment, m_page, this);
 
     LOG(WebReplay, "%-20sLoading segment: %p.\n", "ReplayController", segment.get());
     InspectorInstrumentation::segmentLoaded(&m_page, segment);
index ab0d477..b90e88f 100644 (file)
@@ -35,6 +35,8 @@
 #include "ReplayingInputCursor.h"
 #include "SegmentedInputStorage.h"
 #include <wtf/CurrentTime.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
@@ -57,23 +59,6 @@ ReplaySessionSegment::~ReplaySessionSegment()
 {
 }
 
-PassRefPtr<CapturingInputCursor> ReplaySessionSegment::createCapturingCursor(Page&)
-{
-    ASSERT(m_canCapture);
-    m_canCapture = false;
-    return CapturingInputCursor::create(*m_storage);
-}
-
-PassRefPtr<ReplayingInputCursor> ReplaySessionSegment::createReplayingCursor(Page& page, EventLoopInputDispatcherClient* client)
-{
-    return ReplayingInputCursor::create(*m_storage, page, client);
-}
-
-PassRefPtr<FunctorInputCursor> ReplaySessionSegment::createFunctorCursor()
-{
-    return FunctorInputCursor::create(*m_storage);
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_REPLAY)
index 5de60a1..5d45041 100644 (file)
@@ -30,8 +30,9 @@
 
 #if ENABLE(WEB_REPLAY)
 
-#include <replay/NondeterministicInput.h>
+#include <wtf/Forward.h>
 #include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
@@ -43,20 +44,25 @@ class ReplayingInputCursor;
 class SegmentedInputStorage;
 
 class ReplaySessionSegment : public RefCounted<ReplaySessionSegment> {
+friend class CapturingInputCursor;
+friend class FunctorInputCursor;
+friend class ReplayingInputCursor;
 public:
     static PassRefPtr<ReplaySessionSegment> create();
     ~ReplaySessionSegment();
 
     unsigned identifier() const { return m_identifier; }
     double timestamp() const { return m_timestamp; }
+protected:
+    SegmentedInputStorage& storage() { return *m_storage; }
+    Vector<double, 0>& eventLoopTimings() { return m_eventLoopTimings; }
 
-    PassRefPtr<CapturingInputCursor> createCapturingCursor(Page&);
-    PassRefPtr<ReplayingInputCursor> createReplayingCursor(Page&, EventLoopInputDispatcherClient*);
-    PassRefPtr<FunctorInputCursor> createFunctorCursor();
 private:
     ReplaySessionSegment();
 
     std::unique_ptr<SegmentedInputStorage> m_storage;
+    Vector<double, 0> m_eventLoopTimings;
+
     unsigned m_identifier;
     bool m_canCapture;
     double m_timestamp;
index a4f2db3..1b18c02 100644 (file)
@@ -31,6 +31,7 @@
 #if ENABLE(WEB_REPLAY)
 
 #include "EventLoopInputDispatcher.h"
+#include "ReplaySessionSegment.h"
 #include "SegmentedInputStorage.h"
 #include "SerializationMethods.h"
 #include "WebReplayInputs.h"
@@ -38,8 +39,8 @@
 
 namespace WebCore {
 
-ReplayingInputCursor::ReplayingInputCursor(SegmentedInputStorage& storage, Page& page, EventLoopInputDispatcherClient* client)
-    : m_storage(storage)
+ReplayingInputCursor::ReplayingInputCursor(PassRefPtr<ReplaySessionSegment> segment, Page& page, EventLoopInputDispatcherClient* client)
+    : m_segment(segment)
     , m_dispatcher(std::make_unique<EventLoopInputDispatcher>(page, *this, client))
 {
     for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++)
@@ -50,9 +51,9 @@ ReplayingInputCursor::~ReplayingInputCursor()
 {
 }
 
-PassRefPtr<ReplayingInputCursor> ReplayingInputCursor::create(SegmentedInputStorage& storage, Page& page, EventLoopInputDispatcherClient* client)
+PassRefPtr<ReplayingInputCursor> ReplayingInputCursor::create(PassRefPtr<ReplaySessionSegment> segment, Page& page, EventLoopInputDispatcherClient* client)
 {
-    return adoptRef(new ReplayingInputCursor(storage, page, client));
+    return adoptRef(new ReplayingInputCursor(segment, page, client));
 }
 
 void ReplayingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase>)
@@ -75,13 +76,26 @@ NondeterministicInputBase* ReplayingInputCursor::loadInput(InputQueue queue, con
 
 NondeterministicInputBase* ReplayingInputCursor::uncheckedLoadInput(InputQueue queue)
 {
-    if (m_positions[static_cast<size_t>(queue)] >= m_storage.queueSize(queue)) {
+    if (m_positions[static_cast<size_t>(queue)] >= m_segment->storage().queueSize(queue)) {
         String queueString = EncodingTraits<InputQueue>::encodeValue(queue).convertTo<String>();
         LOG_ERROR("%-30s ERROR No more inputs remain for determinism queue %s, but one was requested.", "[ReplayingInputCursor]", queueString.ascii().data());
         return nullptr;
     }
 
-    return m_storage.load(queue, m_positions[static_cast<size_t>(queue)]++);
+    return m_segment->storage().load(queue, m_positions[static_cast<size_t>(queue)]++);
+}
+
+EventLoopInputData ReplayingInputCursor::loadEventLoopInput()
+{
+    ASSERT(m_segment);
+
+    size_t offset = m_positions.at(static_cast<size_t>(InputQueue::EventLoopInput));
+    ASSERT(offset < m_segment->eventLoopTimings().size());
+
+    EventLoopInputData data;
+    data.timestamp = m_segment->eventLoopTimings().at(offset);
+    data.input = static_cast<EventLoopInputBase*>(uncheckedLoadInput(InputQueue::EventLoopInput));
+    return data;
 }
 
 }; // namespace WebCore
index 14757ba..9c71f41 100644 (file)
 
 namespace WebCore {
 
+class EventLoopInputBase;
 class EventLoopInputDispatcher;
 class EventLoopInputDispatcherClient;
 class Page;
-class SegmentedInputStorage;
+class ReplaySessionSegment;
+
+struct EventLoopInputData {
+    EventLoopInputBase* input;
+    double timestamp;
+};
 
 class ReplayingInputCursor final : public InputCursor {
     WTF_MAKE_NONCOPYABLE(ReplayingInputCursor);
 public:
-    static PassRefPtr<ReplayingInputCursor> create(SegmentedInputStorage&, Page&, EventLoopInputDispatcherClient*);
+    static PassRefPtr<ReplayingInputCursor> create(PassRefPtr<ReplaySessionSegment>, Page&, EventLoopInputDispatcherClient*);
     virtual ~ReplayingInputCursor();
 
     virtual bool isCapturing() const override { return false; }
@@ -52,14 +58,16 @@ public:
 
     EventLoopInputDispatcher& dispatcher() const { return *m_dispatcher; }
 
-    virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
-    virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+    EventLoopInputData loadEventLoopInput();
 protected:
     virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString& type) override;
 private:
-    ReplayingInputCursor(SegmentedInputStorage&, Page&, EventLoopInputDispatcherClient*);
+    ReplayingInputCursor(PassRefPtr<ReplaySessionSegment>, Page&, EventLoopInputDispatcherClient*);
+
+    virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
+    virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
 
-    SegmentedInputStorage& m_storage;
+    RefPtr<ReplaySessionSegment> m_segment;
     std::unique_ptr<EventLoopInputDispatcher> m_dispatcher;
     Vector<size_t> m_positions;
 };