summaryrefslogtreecommitdiff
path: root/Source/WebCore/replay
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/replay')
-rw-r--r--Source/WebCore/replay/AllReplayInputs.h45
-rw-r--r--Source/WebCore/replay/CapturingInputCursor.cpp86
-rw-r--r--Source/WebCore/replay/CapturingInputCursor.h65
-rw-r--r--Source/WebCore/replay/EventLoopInput.cpp53
-rw-r--r--Source/WebCore/replay/EventLoopInput.h75
-rw-r--r--Source/WebCore/replay/EventLoopInputDispatcher.cpp173
-rw-r--r--Source/WebCore/replay/EventLoopInputDispatcher.h96
-rw-r--r--Source/WebCore/replay/FunctorInputCursor.h99
-rw-r--r--Source/WebCore/replay/MemoizedDOMResult.cpp113
-rw-r--r--Source/WebCore/replay/MemoizedDOMResult.h161
-rw-r--r--Source/WebCore/replay/ReplayController.cpp544
-rw-r--r--Source/WebCore/replay/ReplayController.h197
-rw-r--r--Source/WebCore/replay/ReplayInputCreationMethods.cpp54
-rw-r--r--Source/WebCore/replay/ReplayInputDispatchMethods.cpp99
-rw-r--r--Source/WebCore/replay/ReplaySession.cpp93
-rw-r--r--Source/WebCore/replay/ReplaySession.h71
-rw-r--r--Source/WebCore/replay/ReplaySessionSegment.cpp61
-rw-r--r--Source/WebCore/replay/ReplaySessionSegment.h72
-rw-r--r--Source/WebCore/replay/ReplayingInputCursor.cpp103
-rw-r--r--Source/WebCore/replay/ReplayingInputCursor.h75
-rw-r--r--Source/WebCore/replay/SegmentedInputStorage.cpp123
-rw-r--r--Source/WebCore/replay/SegmentedInputStorage.h59
-rw-r--r--Source/WebCore/replay/SerializationMethods.cpp538
-rw-r--r--Source/WebCore/replay/SerializationMethods.h121
-rw-r--r--Source/WebCore/replay/UserInputBridge.cpp260
-rw-r--r--Source/WebCore/replay/UserInputBridge.h102
-rw-r--r--Source/WebCore/replay/WebInputs.json244
27 files changed, 3782 insertions, 0 deletions
diff --git a/Source/WebCore/replay/AllReplayInputs.h b/Source/WebCore/replay/AllReplayInputs.h
new file mode 100644
index 000000000..7b52be6be
--- /dev/null
+++ b/Source/WebCore/replay/AllReplayInputs.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+// This file is the include equivalent for WEB_REPLAY_INPUT_NAMES_FOR_EACH.
+// Note that there is not an exact correspondence between the two, since
+// Some input types reside in the same file.
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include "MemoizedDOMResult.h"
+#include "WebReplayInputs.h"
+#include <JavaScriptCore/JSReplayInputs.h>
+
+#define IMPORT_FROM_JSC_NAMESPACE(name) \
+using JSC::name; \
+
+JS_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_JSC_NAMESPACE)
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/CapturingInputCursor.cpp b/Source/WebCore/replay/CapturingInputCursor.cpp
new file mode 100644
index 000000000..99649bfe2
--- /dev/null
+++ b/Source/WebCore/replay/CapturingInputCursor.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "CapturingInputCursor.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "EventLoopInput.h"
+#include "Logging.h"
+#include "ReplaySessionSegment.h"
+#include "SegmentedInputStorage.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+CapturingInputCursor::CapturingInputCursor(RefPtr<ReplaySessionSegment>&& segment)
+ : m_segment(WTFMove(segment))
+{
+ LOG(WebReplay, "%-30sCreated capture cursor=%p.\n", "[ReplayController]", this);
+}
+
+CapturingInputCursor::~CapturingInputCursor()
+{
+ LOG(WebReplay, "%-30sDestroyed capture cursor=%p.\n", "[ReplayController]", this);
+}
+
+Ref<CapturingInputCursor> CapturingInputCursor::create(RefPtr<ReplaySessionSegment>&& segment)
+{
+ return adoptRef(*new CapturingInputCursor(WTFMove(segment)));
+}
+
+void CapturingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase> 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(WTFMove(input));
+}
+
+NondeterministicInputBase* CapturingInputCursor::loadInput(InputQueue, const String&)
+{
+ // Can't load inputs from capturing cursor.
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+NondeterministicInputBase* CapturingInputCursor::uncheckedLoadInput(InputQueue)
+{
+ // Can't load inputs from capturing cursor.
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+}; // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/CapturingInputCursor.h b/Source/WebCore/replay/CapturingInputCursor.h
new file mode 100644
index 000000000..01ce7b56e
--- /dev/null
+++ b/Source/WebCore/replay/CapturingInputCursor.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <replay/InputCursor.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class EventLoopInputExtent;
+class Page;
+class ReplaySessionSegment;
+
+class CapturingInputCursor final : public InputCursor {
+ WTF_MAKE_NONCOPYABLE(CapturingInputCursor);
+public:
+ static Ref<CapturingInputCursor> create(RefPtr<ReplaySessionSegment>&&);
+ virtual ~CapturingInputCursor();
+
+ bool isCapturing() const override { return true; }
+ bool isReplaying() const override { return false; }
+
+protected:
+ NondeterministicInputBase* loadInput(InputQueue, const String& type) override;
+
+private:
+ CapturingInputCursor(RefPtr<ReplaySessionSegment>&&);
+
+ NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+ void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
+
+ RefPtr<ReplaySessionSegment> m_segment;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/EventLoopInput.cpp b/Source/WebCore/replay/EventLoopInput.cpp
new file mode 100644
index 000000000..2599a0aee
--- /dev/null
+++ b/Source/WebCore/replay/EventLoopInput.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <replay/InputCursor.h>
+
+namespace WebCore {
+
+EventLoopInputExtent::EventLoopInputExtent(JSC::InputCursor& cursor)
+ : EventLoopInputExtent(&cursor) { }
+
+EventLoopInputExtent::EventLoopInputExtent(JSC::InputCursor* cursor)
+ : m_cursor(cursor)
+{
+ if (m_cursor)
+ m_cursor->setWithinEventLoopInputExtent(true);
+}
+
+EventLoopInputExtent::~EventLoopInputExtent()
+{
+ if (m_cursor)
+ m_cursor->setWithinEventLoopInputExtent(false);
+}
+
+}; // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/EventLoopInput.h b/Source/WebCore/replay/EventLoopInput.h
new file mode 100644
index 000000000..53a978e77
--- /dev/null
+++ b/Source/WebCore/replay/EventLoopInput.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#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(JSC::InputCursor*);
+ ~EventLoopInputExtent();
+private:
+ JSC::InputCursor* m_cursor;
+};
+
+class EventLoopInputBase : public NondeterministicInputBase {
+public:
+ virtual ~EventLoopInputBase() { }
+ InputQueue queue() const final { return InputQueue::EventLoopInput; }
+
+ virtual void dispatch(ReplayController&) = 0;
+};
+
+template <typename InputType>
+class EventLoopInput : public EventLoopInputBase {
+public:
+ const String& type() const final
+ {
+ return InputTraits<InputType>::type();
+ }
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/EventLoopInputDispatcher.cpp b/Source/WebCore/replay/EventLoopInputDispatcher.cpp
new file mode 100644
index 000000000..510293fc1
--- /dev/null
+++ b/Source/WebCore/replay/EventLoopInputDispatcher.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "EventLoopInputDispatcher.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "Page.h"
+#include "ReplayingInputCursor.h"
+#include "WebReplayInputs.h"
+#include <wtf/SetForScope.h>
+
+#if !LOG_DISABLED
+#include "Logging.h"
+#include "SerializationMethods.h"
+#include <replay/EncodedValue.h>
+#include <wtf/text/CString.h>
+#endif
+
+namespace WebCore {
+
+EventLoopInputDispatcher::EventLoopInputDispatcher(Page& page, ReplayingInputCursor& cursor, EventLoopInputDispatcherClient* client)
+ : m_page(page)
+ , m_client(client)
+ , m_cursor(cursor)
+ , m_timer(*this, &EventLoopInputDispatcher::timerFired)
+ , m_speed(DispatchSpeed::FastForward)
+{
+ m_currentWork.input = nullptr;
+ m_currentWork.timestamp = 0.0;
+}
+
+void EventLoopInputDispatcher::run()
+{
+ ASSERT(!m_running);
+ m_running = true;
+
+ LOG(WebReplay, "%-20s Starting dispatch of event loop inputs for page: %p\n", "ReplayEvents", &m_page);
+ dispatchInputSoon();
+}
+
+void EventLoopInputDispatcher::pause()
+{
+ ASSERT(!m_dispatching);
+ ASSERT(m_running);
+ m_running = false;
+
+ LOG(WebReplay, "%-20s Pausing dispatch of event loop inputs for page: %p\n", "ReplayEvents", &m_page);
+ if (m_timer.isActive())
+ m_timer.stop();
+}
+
+void EventLoopInputDispatcher::timerFired()
+{
+ dispatchInput();
+}
+
+void EventLoopInputDispatcher::dispatchInputSoon()
+{
+ ASSERT(m_running);
+
+ // We may already have an input if replay was paused just before dispatching.
+ if (!m_currentWork.input)
+ m_currentWork = m_cursor.loadEventLoopInput();
+
+ if (m_timer.isActive())
+ m_timer.stop();
+
+ double waitInterval = 0;
+
+ if (m_speed == DispatchSpeed::RealTime) {
+ // The goal is to reproduce the dispatch delay between inputs as it was
+ // was observed during the recording. So, we need to compute how much time
+ // to wait such that the elapsed time plus the wait time will equal the
+ // observed delay between the previous and current input.
+
+ if (!m_previousInputTimestamp)
+ m_previousInputTimestamp = m_currentWork.timestamp;
+
+ double targetInterval = m_currentWork.timestamp - m_previousInputTimestamp;
+ double elapsed = monotonicallyIncreasingTime() - m_previousDispatchStartTime;
+ waitInterval = targetInterval - elapsed;
+ }
+
+ // A negative wait time means that dispatch took longer on replay than on
+ // capture. In this case, proceed without waiting at all.
+ if (waitInterval < 0)
+ waitInterval = 0;
+
+ if (waitInterval > 1000.0) {
+ LOG_ERROR("%-20s Tried to wait for over 1000 seconds before dispatching next event loop input; this is probably a bug.", "ReplayEvents");
+ waitInterval = 0;
+ }
+
+ LOG(WebReplay, "%-20s (WAIT: %.3f ms)", "ReplayEvents", waitInterval * 1000.0);
+ m_timer.startOneShot(waitInterval);
+}
+
+void EventLoopInputDispatcher::dispatchInput()
+{
+ ASSERT(m_currentWork.input);
+ ASSERT(!m_dispatching);
+
+ if (m_speed == DispatchSpeed::RealTime) {
+ m_previousDispatchStartTime = monotonicallyIncreasingTime();
+ m_previousInputTimestamp = m_currentWork.timestamp;
+ }
+
+#if !LOG_DISABLED
+ 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_currentWork.input->type().utf8().data(), jsonString.utf8().data());
+#endif
+
+ m_client->willDispatchInput(*m_currentWork.input);
+ // Client could stop replay in the previous callback, so check again.
+ if (!m_running)
+ return;
+
+ {
+ SetForScope<bool> change(m_dispatching, true);
+ m_currentWork.input->dispatch(m_page.replayController());
+ }
+
+ EventLoopInputBase* dispatchedInput = m_currentWork.input;
+ m_currentWork.input = nullptr;
+
+ // Notify clients that the event was dispatched.
+ m_client->didDispatchInput(*dispatchedInput);
+ if (dispatchedInput->type() == InputTraits<EndSegmentSentinel>::type()) {
+ m_running = false;
+ m_dispatching = false;
+ m_client->didDispatchFinalInput();
+ return;
+ }
+
+ // Clients could stop replay during event dispatch, or from any callback above.
+ if (!m_running)
+ return;
+
+ dispatchInputSoon();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/EventLoopInputDispatcher.h b/Source/WebCore/replay/EventLoopInputDispatcher.h
new file mode 100644
index 000000000..2067e78d4
--- /dev/null
+++ b/Source/WebCore/replay/EventLoopInputDispatcher.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include "EventLoopInput.h"
+#include "ReplayingInputCursor.h"
+#include "Timer.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class Page;
+
+enum class DispatchSpeed {
+ RealTime,
+ FastForward,
+};
+
+class EventLoopInputDispatcherClient {
+public:
+ EventLoopInputDispatcherClient() { }
+ virtual ~EventLoopInputDispatcherClient() { }
+
+ virtual void willDispatchInput(const EventLoopInputBase&) =0;
+ virtual void didDispatchInput(const EventLoopInputBase&) =0;
+ virtual void didDispatchFinalInput() =0;
+};
+
+class EventLoopInputDispatcher {
+ WTF_MAKE_NONCOPYABLE(EventLoopInputDispatcher);
+public:
+ EventLoopInputDispatcher(Page&, ReplayingInputCursor&, EventLoopInputDispatcherClient*);
+
+ void run();
+ void pause();
+
+ void setDispatchSpeed(DispatchSpeed speed) { m_speed = speed; }
+ DispatchSpeed dispatchSpeed() const { return m_speed; }
+
+ bool isRunning() const { return m_running; }
+ bool isDispatching() const { return m_dispatching; }
+private:
+ void dispatchInputSoon();
+ void dispatchInput();
+ void timerFired();
+
+ Page& m_page;
+ EventLoopInputDispatcherClient* m_client;
+ ReplayingInputCursor& m_cursor;
+ Timer m_timer;
+
+ // 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 {false};
+ // Whether the dispatcher is waiting to dispatch or actively dispatching inputs.
+ bool m_running {false};
+
+ DispatchSpeed m_speed;
+ // The time at which the last input dispatch() method was called.
+ double m_previousDispatchStartTime {0.0};
+ // The timestamp specified by the last dispatched input.
+ double m_previousInputTimestamp {0.0};
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/FunctorInputCursor.h b/Source/WebCore/replay/FunctorInputCursor.h
new file mode 100644
index 000000000..6f82f1fb6
--- /dev/null
+++ b/Source/WebCore/replay/FunctorInputCursor.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include "ReplaySessionSegment.h"
+#include "SegmentedInputStorage.h"
+#include <replay/InputCursor.h>
+#include <replay/NondeterministicInput.h>
+#include <wtf/Assertions.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class FunctorInputCursor final : public InputCursor {
+ WTF_MAKE_NONCOPYABLE(FunctorInputCursor);
+public:
+ static Ref<FunctorInputCursor> create(RefPtr<ReplaySessionSegment>&& segment)
+ {
+ return adoptRef(*new FunctorInputCursor(WTFMove(segment)));
+ }
+
+ // InputCursor
+ bool isCapturing() const override { return false; }
+ bool isReplaying() const override { return false; }
+
+ void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
+ NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+
+ template<typename Functor>
+ typename Functor::ReturnType forEachInputInQueue(InputQueue, Functor&);
+protected:
+ NondeterministicInputBase* loadInput(InputQueue, const String&) override;
+private:
+ FunctorInputCursor(RefPtr<ReplaySessionSegment>&&);
+
+ RefPtr<ReplaySessionSegment> m_segment;
+};
+
+template<typename Functor> inline
+typename Functor::ReturnType FunctorInputCursor::forEachInputInQueue(InputQueue queue, Functor& functor)
+{
+ 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(RefPtr<ReplaySessionSegment>&& segment)
+ : m_segment(WTFMove(segment))
+{
+}
+
+inline void FunctorInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase>)
+{
+ ASSERT_NOT_REACHED();
+}
+
+inline NondeterministicInputBase* FunctorInputCursor::loadInput(InputQueue, const String&)
+{
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+inline NondeterministicInputBase* FunctorInputCursor::uncheckedLoadInput(InputQueue)
+{
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/MemoizedDOMResult.cpp b/Source/WebCore/replay/MemoizedDOMResult.cpp
new file mode 100644
index 000000000..fdef95d08
--- /dev/null
+++ b/Source/WebCore/replay/MemoizedDOMResult.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "MemoizedDOMResult.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "SerializationMethods.h"
+#include "WebReplayInputs.h"
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+const String& MemoizedDOMResultBase::type() const
+{
+ return InputTraits<MemoizedDOMResultBase>::type();
+}
+
+std::unique_ptr<MemoizedDOMResultBase> MemoizedDOMResultBase::createFromEncodedResult(const String& attribute, EncodedCType ctype, EncodedValue encodedValue, ExceptionCode exceptionCode)
+{
+ switch (ctype) {
+#define CREATE_DECODE_SWITCH_CASE(name, type) \
+ case CTypeTraits<type>::encodedType: { \
+ CTypeTraits<type>::CType result; \
+ if (!EncodingTraits<type>::decodeValue(encodedValue, result)) \
+ return nullptr; \
+ return std::make_unique<MemoizedDOMResult<type>>(attribute, result, exceptionCode); \
+ } \
+\
+
+FOR_EACH_MEMOIZED_CTYPE(CREATE_DECODE_SWITCH_CASE)
+#undef CREATE_DECODE_SWITCH_CASE
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+} // namespace WebCore
+
+namespace JSC {
+
+using WebCore::EncodedCType;
+using WebCore::ExceptionCode;
+using WebCore::MemoizedDOMResult;
+using WebCore::SerializedScriptValue;
+
+const String& InputTraits<MemoizedDOMResultBase>::type()
+{
+ static NeverDestroyed<const String> type(ASCIILiteral("MemoizedDOMResult"));
+ return type;
+}
+
+void InputTraits<MemoizedDOMResultBase>::encode(EncodedValue& encodedValue, const MemoizedDOMResultBase& input)
+{
+ encodedValue.put<String>(ASCIILiteral("attribute"), input.attribute());
+ encodedValue.put<EncodedCType>(ASCIILiteral("ctype"), input.ctype());
+ encodedValue.put<EncodedValue>(ASCIILiteral("result"), input.encodedResult());
+ if (input.exceptionCode())
+ encodedValue.put<ExceptionCode>(ASCIILiteral("exceptionCode"), input.exceptionCode());
+}
+
+bool InputTraits<MemoizedDOMResultBase>::decode(EncodedValue& encodedValue, std::unique_ptr<MemoizedDOMResultBase>& input)
+{
+ String attribute;
+ if (!encodedValue.get<String>(ASCIILiteral("attribute"), attribute))
+ return false;
+
+ EncodedCType ctype;
+ if (!encodedValue.get<EncodedCType>(ASCIILiteral("ctype"), ctype))
+ return false;
+
+ EncodedValue encodedResult;
+ if (!encodedValue.get<EncodedValue>(ASCIILiteral("result"), encodedResult))
+ return false;
+
+ ExceptionCode exceptionCode = 0;
+ encodedValue.get<ExceptionCode>(ASCIILiteral("exceptionCode"), exceptionCode);
+
+ std::unique_ptr<MemoizedDOMResultBase> decodedInput = MemoizedDOMResultBase::createFromEncodedResult(attribute, ctype, encodedResult, exceptionCode);
+ if (!decodedInput)
+ return false;
+ input = WTFMove(decodedInput);
+ return true;
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/MemoizedDOMResult.h b/Source/WebCore/replay/MemoizedDOMResult.h
new file mode 100644
index 000000000..b00b15b5a
--- /dev/null
+++ b/Source/WebCore/replay/MemoizedDOMResult.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <replay/EncodedValue.h>
+#include <replay/NondeterministicInput.h>
+#include <wtf/TypeCasts.h>
+
+namespace WebCore {
+
+class SerializedScriptValue;
+
+using ExceptionCode = int;
+
+// Add new memoized ctypes here. The first argument is the enum value,
+// which cannot conflict with built-in primitive types. The second is
+// the actual C type that is used to specialize CTypeTraits. New enum
+// values should also be added to the definition in WebInputs.json.
+#define FOR_EACH_MEMOIZED_CTYPE(macro) \
+ macro(Boolean, bool) \
+ macro(Int, int) \
+ macro(String, String) \
+ macro(Unsigned, unsigned) \
+ \
+// end of FOR_EACH_MEMOIZED_CTYPE
+
+// We encode this enum so that we can recover MemoizedType when decoding the input
+// and then call the correct specialized MemoizedDOMResult<T> constructor.
+enum class EncodedCType {
+#define CREATE_ENUM_VALUE(name, type) name,
+
+FOR_EACH_MEMOIZED_CTYPE(CREATE_ENUM_VALUE)
+#undef CREATE_ENUM_VALUE
+};
+
+class MemoizedDOMResultBase : public NondeterministicInputBase {
+public:
+ MemoizedDOMResultBase(const String& attribute, EncodedCType ctype, ExceptionCode exceptionCode = 0)
+ : m_attribute(attribute)
+ , m_ctype(ctype)
+ , m_exceptionCode(exceptionCode) { }
+
+ virtual ~MemoizedDOMResultBase() { }
+
+ static std::unique_ptr<MemoizedDOMResultBase> createFromEncodedResult(const String& attribute, EncodedCType, EncodedValue, ExceptionCode);
+
+ template<typename T>
+ bool convertTo(T& decodedValue);
+
+ virtual EncodedValue encodedResult() const = 0;
+ InputQueue queue() const final { return InputQueue::ScriptMemoizedData; }
+ const String& type() const final;
+
+ const String& attribute() const { return m_attribute; }
+ EncodedCType ctype() const { return m_ctype; }
+ ExceptionCode exceptionCode() const { return m_exceptionCode; }
+private:
+ String m_attribute;
+ EncodedCType m_ctype;
+ ExceptionCode m_exceptionCode;
+};
+
+template<typename T>
+struct CTypeTraits {
+ static bool decode(EncodedValue& encodedValue, T& decodedValue)
+ {
+ return EncodingTraits<T>::decodeValue(encodedValue, decodedValue);
+ }
+};
+
+#define CREATE_CTYPE_TRAITS(_name, _type) \
+template<> \
+struct CTypeTraits<_type> { \
+ typedef _type CType; \
+ static const EncodedCType encodedType = EncodedCType::_name; \
+}; \
+
+FOR_EACH_MEMOIZED_CTYPE(CREATE_CTYPE_TRAITS)
+#undef CREATE_CTYPE_TRAITS
+
+template<typename MemoizedType>
+class MemoizedDOMResult final : public MemoizedDOMResultBase {
+public:
+ MemoizedDOMResult(const String& attribute, typename CTypeTraits<MemoizedType>::CType result, ExceptionCode exceptionCode)
+ : MemoizedDOMResultBase(attribute, CTypeTraits<MemoizedType>::encodedType, exceptionCode)
+ , m_result(result) { }
+ virtual ~MemoizedDOMResult() { }
+
+ EncodedValue encodedResult() const override
+ {
+ return EncodingTraits<MemoizedType>::encodeValue(m_result);
+ }
+
+ typename CTypeTraits<MemoizedType>::CType result() const { return m_result; }
+private:
+ typename CTypeTraits<MemoizedType>::CType m_result;
+};
+
+// This is used by clients of the memoized DOM result to get out the memoized
+// value without performing a cast to MemoizedDOMResult<T> and calling result().
+template<typename T>
+bool MemoizedDOMResultBase::convertTo(T& convertedValue)
+{
+ // Type tag doesn't match; fail to decode the value.
+ if (m_ctype != CTypeTraits<T>::encodedType)
+ return false;
+
+ MemoizedDOMResult<T>& castedResult = static_cast<MemoizedDOMResult<T>&>(*this);
+ convertedValue = castedResult.result();
+ return true;
+}
+
+} // namespace WebCore
+
+using WebCore::MemoizedDOMResultBase;
+
+namespace JSC {
+
+template<>
+struct InputTraits<MemoizedDOMResultBase> {
+ static InputQueue queue() { return InputQueue::ScriptMemoizedData; }
+ static const String& type();
+
+ static void encode(EncodedValue&, const MemoizedDOMResultBase& input);
+ static bool decode(EncodedValue&, std::unique_ptr<MemoizedDOMResultBase>& input);
+};
+
+} // namespace JSC
+
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::MemoizedDOMResultBase)
+static bool isType(const NondeterministicInputBase& input) { return input.type() == InputTraits<WebCore::MemoizedDOMResultBase>::type(); }
+SPECIALIZE_TYPE_TRAITS_END()
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayController.cpp b/Source/WebCore/replay/ReplayController.cpp
new file mode 100644
index 000000000..bf358dc6a
--- /dev/null
+++ b/Source/WebCore/replay/ReplayController.cpp
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "ReplayController.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "AllReplayInputs.h"
+#include "CapturingInputCursor.h"
+#include "DOMWindow.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "InspectorInstrumentation.h"
+#include "Location.h"
+#include "Logging.h"
+#include "MainFrame.h"
+#include "Page.h"
+#include "ReplaySession.h"
+#include "ReplaySessionSegment.h"
+#include "ReplayingInputCursor.h"
+#include "ScriptController.h"
+#include "SerializationMethods.h"
+#include "Settings.h"
+#include "UserInputBridge.h"
+#include "WebReplayInputs.h"
+#include <replay/EmptyInputCursor.h>
+#include <wtf/text/CString.h>
+
+#if ENABLE(ASYNC_SCROLLING)
+#include "ScrollingCoordinator.h"
+#endif
+
+namespace WebCore {
+
+#if !LOG_DISABLED
+static void logDispatchedDOMEvent(const Event& event, bool eventIsUnrelated)
+{
+ 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=%u/node[%p] %s\n", "ReplayEvents",
+ (eventIsUnrelated) ? "Unrelated" : "Dispatching",
+ event.type().string().utf8().data(),
+ frameIndexFromDocument((node->isConnected()) ? &node->document() : node->ownerDocument()),
+ node,
+ node->nodeName().utf8().data());
+ } else if (DOMWindow* window = target->toDOMWindow()) {
+ LOG(WebReplay, "%-20s --->%s DOM event: type=%s, target=%u/window[%p] %s\n", "ReplayEvents",
+ (eventIsUnrelated) ? "Unrelated" : "Dispatching",
+ event.type().string().utf8().data(),
+ frameIndexFromDocument(window->document()),
+ window,
+ window->location()->href().utf8().data());
+ }
+}
+
+static const char* sessionStateToString(SessionState state)
+{
+ switch (state) {
+ case SessionState::Capturing:
+ return "Capturing";
+ case SessionState::Inactive:
+ return "Inactive";
+ case SessionState::Replaying:
+ return "Replaying";
+ }
+}
+
+static const char* segmentStateToString(SegmentState state)
+{
+ switch (state) {
+ case SegmentState::Appending:
+ return "Appending";
+ case SegmentState::Unloaded:
+ return "Unloaded";
+ case SegmentState::Loaded:
+ return "Loaded";
+ case SegmentState::Dispatching:
+ return "Dispatching";
+ }
+}
+
+#endif // !LOG_DISABLED
+
+ReplayController::ReplayController(Page& page)
+ : m_page(page)
+ , m_loadedSession(ReplaySession::create())
+ , m_emptyCursor(EmptyInputCursor::create())
+ , m_targetPosition(ReplayPosition(0, 0))
+ , m_currentPosition(ReplayPosition(0, 0))
+ , m_segmentState(SegmentState::Unloaded)
+ , m_sessionState(SessionState::Inactive)
+ , m_dispatchSpeed(DispatchSpeed::FastForward)
+{
+}
+
+void ReplayController::setForceDeterministicSettings(bool shouldForceDeterministicBehavior)
+{
+ ASSERT_ARG(shouldForceDeterministicBehavior, shouldForceDeterministicBehavior ^ (m_sessionState == SessionState::Inactive));
+
+ if (shouldForceDeterministicBehavior) {
+ m_savedSettings.usesPageCache = m_page.settings().usesPageCache();
+
+ m_page.settings().setUsesPageCache(false);
+ } else {
+ m_page.settings().setUsesPageCache(m_savedSettings.usesPageCache);
+ }
+
+#if ENABLE(ASYNC_SCROLLING)
+ if (ScrollingCoordinator* scrollingCoordinator = m_page.scrollingCoordinator())
+ scrollingCoordinator->replaySessionStateDidChange();
+#endif
+}
+
+void ReplayController::setSessionState(SessionState state)
+{
+ ASSERT_ARG(state, state != m_sessionState);
+
+ LOG(WebReplay, "%-20s SessionState transition: %10s --> %10s.\n", "ReplayController", sessionStateToString(m_sessionState), sessionStateToString(state));
+
+ switch (m_sessionState) {
+ case SessionState::Capturing:
+ ASSERT(state == SessionState::Inactive);
+
+ m_sessionState = state;
+ m_page.userInputBridge().setState(UserInputBridge::State::Open);
+ break;
+
+ case SessionState::Inactive:
+ m_sessionState = state;
+ m_page.userInputBridge().setState(state == SessionState::Capturing ? UserInputBridge::State::Capturing : UserInputBridge::State::Replaying);
+ break;
+
+ case SessionState::Replaying:
+ ASSERT(state == SessionState::Inactive);
+
+ m_sessionState = state;
+ m_page.userInputBridge().setState(UserInputBridge::State::Open);
+ break;
+ }
+}
+
+void ReplayController::setSegmentState(SegmentState state)
+{
+ ASSERT_ARG(state, state != m_segmentState);
+
+ LOG(WebReplay, "%-20s SegmentState transition: %10s --> %10s.\n", "ReplayController", segmentStateToString(m_segmentState), segmentStateToString(state));
+
+ switch (m_segmentState) {
+ case SegmentState::Appending:
+ ASSERT(state == SegmentState::Unloaded);
+ break;
+
+ case SegmentState::Unloaded:
+ ASSERT(state == SegmentState::Appending || state == SegmentState::Loaded);
+ break;
+
+ case SegmentState::Loaded:
+ ASSERT(state == SegmentState::Unloaded || state == SegmentState::Dispatching);
+ break;
+
+ case SegmentState::Dispatching:
+ ASSERT(state == SegmentState::Loaded);
+ break;
+ }
+
+ m_segmentState = state;
+}
+
+void ReplayController::switchSession(RefPtr<ReplaySession>&& session)
+{
+ ASSERT_ARG(session, session);
+ ASSERT(m_segmentState == SegmentState::Unloaded);
+ ASSERT(m_sessionState == SessionState::Inactive);
+
+ m_loadedSession = session;
+ m_currentPosition = ReplayPosition(0, 0);
+
+ LOG(WebReplay, "%-20sSwitching sessions from %p to %p.\n", "ReplayController", m_loadedSession.get(), session.get());
+ InspectorInstrumentation::sessionLoaded(m_page, m_loadedSession.copyRef());
+}
+
+void ReplayController::createSegment()
+{
+ ASSERT(m_sessionState == SessionState::Capturing);
+ ASSERT(m_segmentState == SegmentState::Unloaded);
+
+ setSegmentState(SegmentState::Appending);
+
+ // Create a new segment but don't associate it with the current session
+ // until we stop appending to it. This preserves the invariant that
+ // segments associated with a replay session have immutable data.
+ m_loadedSegment = ReplaySessionSegment::create();
+
+ LOG(WebReplay, "%-20s Created segment: %p.\n", "ReplayController", m_loadedSegment.get());
+ InspectorInstrumentation::segmentCreated(m_page, m_loadedSegment.copyRef());
+
+ m_activeCursor = CapturingInputCursor::create(m_loadedSegment.copyRef());
+ m_activeCursor->appendInput<BeginSegmentSentinel>();
+
+ std::unique_ptr<InitialNavigation> navigationInput = InitialNavigation::createFromPage(m_page);
+ // Dispatching this input schedules navigation of the main frame, causing a refresh.
+ navigationInput->dispatch(*this);
+ m_activeCursor->storeInput(WTFMove(navigationInput));
+}
+
+void ReplayController::completeSegment()
+{
+ ASSERT(m_sessionState == SessionState::Capturing);
+ ASSERT(m_segmentState == SegmentState::Appending);
+
+ m_activeCursor->appendInput<EndSegmentSentinel>();
+
+ // Hold on to a reference so unloading the segment doesn't deallocate it.
+ RefPtr<ReplaySessionSegment> segment = m_loadedSegment;
+ bool shouldSuppressNotifications = true;
+ unloadSegment(shouldSuppressNotifications);
+
+ LOG(WebReplay, "%-20s Completed segment: %p.\n", "ReplayController", segment.get());
+ InspectorInstrumentation::segmentCompleted(m_page, segment.copyRef());
+
+ m_loadedSession->appendSegment(segment.copyRef());
+ InspectorInstrumentation::sessionModified(m_page, m_loadedSession.copyRef());
+}
+
+void ReplayController::loadSegmentAtIndex(size_t segmentIndex)
+{
+ ASSERT_ARG(segmentIndex, segmentIndex >= 0 && segmentIndex < m_loadedSession->size());
+ RefPtr<ReplaySessionSegment> segment = m_loadedSession->at(segmentIndex);
+
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Unloaded);
+ ASSERT(segment);
+ ASSERT(!m_loadedSegment);
+
+ m_loadedSegment = segment;
+ setSegmentState(SegmentState::Loaded);
+
+ m_currentPosition.segmentOffset = segmentIndex;
+ m_currentPosition.inputOffset = 0;
+
+ m_activeCursor = ReplayingInputCursor::create(m_loadedSegment.copyRef(), m_page, this);
+
+ LOG(WebReplay, "%-20sLoading segment: %p.\n", "ReplayController", segment.get());
+ InspectorInstrumentation::segmentLoaded(m_page, segment.copyRef());
+}
+
+void ReplayController::unloadSegment(bool suppressNotifications)
+{
+ ASSERT(m_sessionState != SessionState::Inactive);
+ ASSERT(m_segmentState == SegmentState::Loaded || m_segmentState == SegmentState::Appending);
+
+ setSegmentState(SegmentState::Unloaded);
+
+ LOG(WebReplay, "%-20s Clearing input cursors for page: %p\n", "ReplayController", &m_page);
+
+ m_activeCursor = nullptr;
+ auto unloadedSegment = WTFMove(m_loadedSegment);
+ for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
+ frame->script().globalObject(mainThreadNormalWorld())->setInputCursor(m_emptyCursor.copyRef());
+ frame->document()->setInputCursor(m_emptyCursor.copyRef());
+ }
+
+ // When we stop capturing, don't send out segment unloaded events since we
+ // didn't send out the corresponding segmentLoaded event at the start of capture.
+ if (!suppressNotifications) {
+ LOG(WebReplay, "%-20sUnloading segment: %p.\n", "ReplayController", unloadedSegment.get());
+ InspectorInstrumentation::segmentUnloaded(m_page);
+ }
+}
+
+void ReplayController::startCapturing()
+{
+ ASSERT(m_sessionState == SessionState::Inactive);
+ ASSERT(m_segmentState == SegmentState::Unloaded);
+
+ setSessionState(SessionState::Capturing);
+ setForceDeterministicSettings(true);
+
+ LOG(WebReplay, "%-20s Starting capture.\n", "ReplayController");
+ InspectorInstrumentation::captureStarted(m_page);
+
+ m_currentPosition = ReplayPosition(0, 0);
+
+ createSegment();
+}
+
+void ReplayController::stopCapturing()
+{
+ ASSERT(m_sessionState == SessionState::Capturing);
+ ASSERT(m_segmentState == SegmentState::Appending);
+
+ completeSegment();
+
+ setSessionState(SessionState::Inactive);
+ setForceDeterministicSettings(false);
+
+ LOG(WebReplay, "%-20s Stopping capture.\n", "ReplayController");
+ InspectorInstrumentation::captureStopped(m_page);
+}
+
+void ReplayController::startPlayback()
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Loaded);
+
+ setSegmentState(SegmentState::Dispatching);
+
+ LOG(WebReplay, "%-20s Starting playback to position (segment: %d, input: %d).\n", "ReplayController", m_targetPosition.segmentOffset, m_targetPosition.inputOffset);
+ InspectorInstrumentation::playbackStarted(m_page);
+
+ dispatcher().setDispatchSpeed(m_dispatchSpeed);
+ dispatcher().run();
+}
+
+void ReplayController::pausePlayback()
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Dispatching);
+
+ if (dispatcher().isRunning())
+ dispatcher().pause();
+
+ setSegmentState(SegmentState::Loaded);
+
+ LOG(WebReplay, "%-20s Pausing playback at position (segment: %d, input: %d).\n", "ReplayController", m_currentPosition.segmentOffset, m_currentPosition.inputOffset);
+ InspectorInstrumentation::playbackPaused(m_page, m_currentPosition);
+}
+
+void ReplayController::cancelPlayback()
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState != SegmentState::Appending);
+
+ if (m_segmentState == SegmentState::Unloaded)
+ return;
+
+ if (m_segmentState == SegmentState::Dispatching)
+ pausePlayback();
+
+ ASSERT(m_segmentState == SegmentState::Loaded);
+ unloadSegment();
+ m_sessionState = SessionState::Inactive;
+ setForceDeterministicSettings(false);
+ InspectorInstrumentation::playbackFinished(m_page);
+}
+
+void ReplayController::replayToPosition(const ReplayPosition& position, DispatchSpeed speed)
+{
+ ASSERT(m_sessionState != SessionState::Capturing);
+ ASSERT(m_segmentState == SegmentState::Loaded || m_segmentState == SegmentState::Unloaded);
+ ASSERT(position.segmentOffset < m_loadedSession->size());
+
+ m_dispatchSpeed = speed;
+
+ if (m_sessionState != SessionState::Replaying) {
+ setSessionState(SessionState::Replaying);
+ setForceDeterministicSettings(true);
+ }
+
+ if (m_segmentState == SegmentState::Unloaded)
+ loadSegmentAtIndex(position.segmentOffset);
+ else if (position.segmentOffset != m_currentPosition.segmentOffset || m_currentPosition.inputOffset > position.inputOffset) {
+ // If the desired segment is not loaded or we have gone past the desired input
+ // offset, then unload the current segment and load the appropriate segment.
+ unloadSegment();
+ loadSegmentAtIndex(position.segmentOffset);
+ }
+
+ ASSERT(m_currentPosition.segmentOffset == position.segmentOffset);
+ ASSERT(m_loadedSession->at(position.segmentOffset) == m_loadedSegment);
+
+ m_targetPosition = position;
+ startPlayback();
+}
+
+void ReplayController::frameNavigated(Frame& frame)
+{
+ ASSERT(m_sessionState != SessionState::Inactive);
+
+ // The initial capturing segment is created prior to main frame navigation.
+ // Otherwise, the prior capturing segment was completed when the frame detached,
+ // and it is now time to create a new segment.
+ if (m_sessionState == SessionState::Capturing && m_segmentState == SegmentState::Unloaded) {
+ m_currentPosition = ReplayPosition(m_currentPosition.segmentOffset + 1, 0);
+ createSegment();
+ }
+
+ // During playback, the next segment is loaded when the final input is dispatched,
+ // so nothing needs to be done here.
+
+ // We store the input cursor in both Document and JSDOMWindow, so that
+ // replay state is accessible from JavaScriptCore and script-free layout code.
+ frame.document()->setInputCursor(*m_activeCursor);
+ frame.script().globalObject(mainThreadNormalWorld())->setInputCursor(*m_activeCursor);
+}
+
+void ReplayController::frameDetached(Frame& frame)
+{
+ ASSERT(m_sessionState != SessionState::Inactive);
+
+ if (!frame.document())
+ return;
+
+ // If the frame's cursor isn't capturing or replaying, we should do nothing.
+ // This is the case for the "outbound" frame when starting capture, or when
+ // we clear the input cursor to finish or prematurely unload a segment.
+ if (frame.document()->inputCursor().isCapturing()) {
+ ASSERT(m_segmentState == SegmentState::Appending);
+ completeSegment();
+ }
+
+ // During playback, the segments are unloaded and loaded when the final
+ // 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->isConnected() ? &node->document() : node->ownerDocument();
+ else if (DOMWindow* window = target->toDOMWindow())
+ document = window->document();
+
+ ASSERT(document);
+ InputCursor& cursor = document->inputCursor();
+
+#if !LOG_DISABLED
+ bool eventIsUnrelated = !cursor.isCapturing() && !cursor.isReplaying();
+ logDispatchedDOMEvent(event, eventIsUnrelated);
+#else
+ UNUSED_PARAM(cursor);
+#endif
+
+#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() || cursor.isReplaying())
+ ASSERT(cursor.withinEventLoopInputExtent());
+ else if (cursor.isReplaying())
+ ASSERT(dispatcher().isDispatching());
+#endif
+}
+
+RefPtr<ReplaySession> ReplayController::loadedSession() const
+{
+ return m_loadedSession.copyRef();
+}
+
+RefPtr<ReplaySessionSegment> ReplayController::loadedSegment() const
+{
+ return m_loadedSegment.copyRef();
+}
+
+InputCursor& ReplayController::activeInputCursor()
+{
+ return m_activeCursor ? *m_activeCursor : m_emptyCursor.get();
+}
+
+EventLoopInputDispatcher& ReplayController::dispatcher() const
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Dispatching);
+ ASSERT(m_activeCursor && m_activeCursor->isReplaying());
+
+ return static_cast<ReplayingInputCursor&>(*m_activeCursor).dispatcher();
+}
+
+void ReplayController::willDispatchInput(const EventLoopInputBase&)
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Dispatching);
+
+ m_currentPosition.inputOffset++;
+
+ InspectorInstrumentation::playbackHitPosition(m_page, m_currentPosition);
+
+ if (m_currentPosition == m_targetPosition)
+ pausePlayback();
+}
+
+void ReplayController::didDispatchInput(const EventLoopInputBase&)
+{
+ ASSERT(m_sessionState == SessionState::Replaying);
+ ASSERT(m_segmentState == SegmentState::Dispatching);
+}
+
+void ReplayController::didDispatchFinalInput()
+{
+ ASSERT(m_segmentState == SegmentState::Dispatching);
+
+ // No more segments left to replay; stop.
+ if (m_currentPosition.segmentOffset + 1 == m_loadedSession->size()) {
+ // Normally the position is adjusted when loading the next segment.
+ m_currentPosition.segmentOffset++;
+ m_currentPosition.inputOffset = 0;
+
+ cancelPlayback();
+ return;
+ }
+
+ unloadSegment();
+ loadSegmentAtIndex(m_currentPosition.segmentOffset + 1);
+ startPlayback();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayController.h b/Source/WebCore/replay/ReplayController.h
new file mode 100644
index 000000000..f4eff4649
--- /dev/null
+++ b/Source/WebCore/replay/ReplayController.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include "EventLoopInputDispatcher.h"
+#include <wtf/Noncopyable.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;
+}
+
+namespace WebCore {
+
+class DOMWindow;
+class Document;
+class DocumentLoader;
+class Element;
+class Event;
+class EventLoopInputBase;
+class Frame;
+class Node;
+class Page;
+class ReplaySession;
+class ReplaySessionSegment;
+
+// Each state may transition to the state immediately above or below it.
+// SessionState transitions are only allowed when SegmentState is Unloaded.
+enum class SessionState {
+ Capturing,
+ // Neither capturing or replaying. m_currentPosition is not valid in this state.
+ Inactive,
+ Replaying,
+};
+
+// Each state may transition to the state immediately above or below it.
+enum class SegmentState {
+ // Inputs can be appended into an unassociated session segment.
+ // We can stop capturing, which reverts to the Unloaded state.
+ Appending,
+ // No session segment is loaded.
+ // We can start capturing, or load a segment (and then replay it).
+ Unloaded,
+ // A session segment is loaded.
+ // We can unload the segment, or begin playback from m_currentPosition.
+ Loaded,
+ // The controller is actively dispatching event loop inputs.
+ // We can pause or cancel playback, which reverts to the Loaded state.
+ Dispatching,
+};
+
+struct ReplayPosition {
+ ReplayPosition(unsigned segmentOffset, unsigned inputOffset)
+ : segmentOffset(segmentOffset)
+ , inputOffset(inputOffset)
+ {
+ }
+
+ // By convention, this position represents the end of the last segment of the session.
+ ReplayPosition()
+ : segmentOffset(0)
+ , inputOffset(0)
+ {
+ }
+
+ bool operator<(const ReplayPosition& other)
+ {
+ return segmentOffset <= other.segmentOffset && inputOffset < other.inputOffset;
+ }
+
+ bool operator==(const ReplayPosition& other)
+ {
+ return segmentOffset == other.segmentOffset && inputOffset == other.inputOffset;
+ }
+
+ unsigned segmentOffset;
+ unsigned inputOffset;
+};
+
+class ReplayController final : public EventLoopInputDispatcherClient {
+ WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(ReplayController);
+public:
+ ReplayController(Page&);
+
+ void startCapturing();
+ void stopCapturing();
+
+ // Start or resume playback with default speed and target replay position.
+ void startPlayback();
+ void pausePlayback();
+ void cancelPlayback();
+
+ void replayToPosition(const ReplayPosition&, DispatchSpeed = DispatchSpeed::FastForward);
+ void replayToCompletion(DispatchSpeed speed = DispatchSpeed::FastForward)
+ {
+ replayToPosition(ReplayPosition(), speed);
+ }
+
+ void switchSession(RefPtr<ReplaySession>&&);
+
+ // InspectorReplayAgent notifications.
+ void frameNavigated(Frame&);
+ void frameDetached(Frame&);
+ void willDispatchEvent(const Event&, Frame*);
+
+ Page& page() const { return m_page; }
+
+ SessionState sessionState() const { return m_sessionState; }
+ SegmentState segmentState() const { return m_segmentState; }
+
+ RefPtr<ReplaySession> loadedSession() const;
+ RefPtr<ReplaySessionSegment> loadedSegment() const;
+
+ JSC::InputCursor& activeInputCursor();
+ ReplayPosition currentPosition() const { return m_currentPosition; }
+
+private:
+ // EventLoopInputDispatcherClient API
+ void willDispatchInput(const EventLoopInputBase&) override;
+ void didDispatchInput(const EventLoopInputBase&) override;
+ void didDispatchFinalInput() override;
+
+ void createSegment();
+ void completeSegment();
+
+ void loadSegmentAtIndex(size_t);
+ void unloadSegment(bool suppressNotifications = false);
+
+ EventLoopInputDispatcher& dispatcher() const;
+
+ void setSessionState(SessionState);
+ void setSegmentState(SegmentState);
+ void setForceDeterministicSettings(bool);
+
+ struct SavedSettings {
+ bool usesPageCache;
+
+ SavedSettings()
+ : usesPageCache(false)
+ { }
+ };
+
+ Page& m_page;
+
+ RefPtr<ReplaySessionSegment> m_loadedSegment;
+ RefPtr<ReplaySession> m_loadedSession;
+ Ref<JSC::InputCursor> m_emptyCursor;
+ // The active cursor is set to nullptr when invalid.
+ RefPtr<JSC::InputCursor> m_activeCursor;
+
+ // This position is valid when SessionState == Replaying.
+ ReplayPosition m_targetPosition;
+ // This position is valid when SessionState != Inactive.
+ ReplayPosition m_currentPosition;
+ SegmentState m_segmentState;
+ // This tracks state across multiple segments. When navigating the main frame,
+ // there is a small interval during segment switching when no segment is loaded.
+ SessionState m_sessionState;
+
+ DispatchSpeed m_dispatchSpeed;
+ SavedSettings m_savedSettings;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayInputCreationMethods.cpp b/Source/WebCore/replay/ReplayInputCreationMethods.cpp
new file mode 100644
index 000000000..f0065c7c3
--- /dev/null
+++ b/Source/WebCore/replay/ReplayInputCreationMethods.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "Document.h"
+#include "MainFrame.h"
+#include "Page.h"
+#include "SecurityOrigin.h"
+#include "SerializationMethods.h"
+#include "WebReplayInputs.h"
+
+namespace WebCore {
+
+std::unique_ptr<InitialNavigation> InitialNavigation::createFromPage(const Page& page)
+{
+ const MainFrame& mainFrame = page.mainFrame();
+ ASSERT(mainFrame.document());
+
+ // Make sure that this is in sync with ReplayController::beginCapturing().
+ RefPtr<SecurityOrigin> originCopy = mainFrame.document()->securityOrigin().isolatedCopy();
+ URL url = mainFrame.document()->url();
+ String referrer = mainFrame.loader().referrer();
+ return std::make_unique<InitialNavigation>(WTFMove(originCopy), url, referrer);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayInputDispatchMethods.cpp b/Source/WebCore/replay/ReplayInputDispatchMethods.cpp
new file mode 100644
index 000000000..044264ee5
--- /dev/null
+++ b/Source/WebCore/replay/ReplayInputDispatchMethods.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011-2013 University of Washington.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "AllReplayInputs.h"
+#include "MainFrame.h"
+#include "NavigationScheduler.h"
+#include "Page.h"
+#include "ReplayController.h"
+#include "URL.h"
+#include "UserInputBridge.h"
+
+namespace WebCore {
+
+// Sentinel inputs.
+void BeginSegmentSentinel::dispatch(ReplayController&)
+{
+}
+
+void EndSegmentSentinel::dispatch(ReplayController&)
+{
+}
+
+// Navigation inputs.
+void InitialNavigation::dispatch(ReplayController& controller)
+{
+ auto& frame = controller.page().mainFrame();
+ ASSERT(frame.document());
+ frame.navigationScheduler().scheduleLocationChange(*frame.document(), *m_securityOrigin, m_url, m_referrer);
+}
+
+void HandleKeyPress::dispatch(ReplayController& controller)
+{
+ controller.page().userInputBridge().handleKeyEvent(platformEvent(), InputSource::Synthetic);
+}
+
+// User interaction inputs.
+void HandleMouseMove::dispatch(ReplayController& controller)
+{
+ if (m_scrollbarTargeted)
+ controller.page().userInputBridge().handleMouseMoveOnScrollbarEvent(platformEvent(), InputSource::Synthetic);
+ else
+ controller.page().userInputBridge().handleMouseMoveEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMousePress::dispatch(ReplayController& controller)
+{
+ controller.page().userInputBridge().handleMousePressEvent(platformEvent(), InputSource::Synthetic);
+}
+
+void HandleMouseRelease::dispatch(ReplayController& controller)
+{
+ controller.page().userInputBridge().handleMouseReleaseEvent(platformEvent(), InputSource::Synthetic);
+}
+
+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)
diff --git a/Source/WebCore/replay/ReplaySession.cpp b/Source/WebCore/replay/ReplaySession.cpp
new file mode 100644
index 000000000..93da28c1c
--- /dev/null
+++ b/Source/WebCore/replay/ReplaySession.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "ReplaySession.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "ReplaySessionSegment.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+static unsigned s_nextIdentifier = 1;
+
+Ref<ReplaySession> ReplaySession::create()
+{
+ return adoptRef(*new ReplaySession());
+}
+
+ReplaySession::ReplaySession()
+ : m_identifier(s_nextIdentifier++)
+ , m_timestamp(currentTimeMS())
+{
+}
+
+ReplaySession::~ReplaySession()
+{
+}
+
+RefPtr<ReplaySessionSegment> ReplaySession::at(size_t position) const
+{
+ ASSERT_ARG(position, position >= 0 && position < m_segments.size());
+
+ return m_segments.at(position).copyRef();
+}
+
+void ReplaySession::appendSegment(RefPtr<ReplaySessionSegment>&& segment)
+{
+ ASSERT_ARG(segment, segment);
+ // For now, only support one segment.
+ ASSERT(!m_segments.size());
+
+ // Since replay locations are specified with segment IDs, we can only
+ // have one instance of a segment in the session.
+ size_t offset = m_segments.find(segment.copyRef());
+ ASSERT_UNUSED(offset, offset == notFound);
+
+ m_segments.append(WTFMove(segment));
+}
+
+void ReplaySession::insertSegment(size_t position, RefPtr<ReplaySessionSegment>&& segment)
+{
+ ASSERT_ARG(segment, segment);
+ ASSERT_ARG(position, position >= 0 && position < m_segments.size());
+
+ m_segments.insert(position, WTFMove(segment));
+}
+
+void ReplaySession::removeSegment(size_t position)
+{
+ ASSERT_ARG(position, position >= 0 && position < m_segments.size());
+
+ m_segments.remove(position);
+}
+
+}; // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplaySession.h b/Source/WebCore/replay/ReplaySession.h
new file mode 100644
index 000000000..2db80102d
--- /dev/null
+++ b/Source/WebCore/replay/ReplaySession.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class ReplaySessionSegment;
+
+typedef Vector<RefPtr<ReplaySessionSegment>>::const_iterator SegmentIterator;
+
+class ReplaySession : public RefCounted<ReplaySession> {
+ WTF_MAKE_NONCOPYABLE(ReplaySession);
+public:
+ static Ref<ReplaySession> create();
+ ~ReplaySession();
+
+ double timestamp() const { return m_timestamp; }
+ unsigned identifier() const { return m_identifier; }
+
+ size_t size() const { return m_segments.size(); }
+ RefPtr<ReplaySessionSegment> at(size_t position) const;
+
+ SegmentIterator begin() const { return m_segments.begin(); }
+ SegmentIterator end() const { return m_segments.end(); }
+
+ void appendSegment(RefPtr<ReplaySessionSegment>&&);
+ void insertSegment(size_t position, RefPtr<ReplaySessionSegment>&&);
+ void removeSegment(size_t position);
+
+private:
+ ReplaySession();
+
+ Vector<RefPtr<ReplaySessionSegment>> m_segments;
+ unsigned m_identifier;
+ double m_timestamp;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplaySessionSegment.cpp b/Source/WebCore/replay/ReplaySessionSegment.cpp
new file mode 100644
index 000000000..f18ddf41f
--- /dev/null
+++ b/Source/WebCore/replay/ReplaySessionSegment.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "ReplaySessionSegment.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "CapturingInputCursor.h"
+#include "FunctorInputCursor.h"
+#include "ReplayingInputCursor.h"
+#include "SegmentedInputStorage.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+static unsigned s_nextSegmentIdentifier = 1;
+
+Ref<ReplaySessionSegment> ReplaySessionSegment::create()
+{
+ return adoptRef(*new ReplaySessionSegment);
+}
+
+ReplaySessionSegment::ReplaySessionSegment()
+ : m_storage(std::make_unique<SegmentedInputStorage>())
+ , m_identifier(s_nextSegmentIdentifier++)
+ , m_timestamp(currentTimeMS())
+{
+}
+
+ReplaySessionSegment::~ReplaySessionSegment()
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplaySessionSegment.h b/Source/WebCore/replay/ReplaySessionSegment.h
new file mode 100644
index 000000000..d562b53c0
--- /dev/null
+++ b/Source/WebCore/replay/ReplaySessionSegment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CapturingInputCursor;
+class EventLoopInputDispatcherClient;
+class FunctorInputCursor;
+class Page;
+class ReplayingInputCursor;
+class SegmentedInputStorage;
+
+class ReplaySessionSegment : public RefCounted<ReplaySessionSegment> {
+friend class CapturingInputCursor;
+friend class FunctorInputCursor;
+friend class ReplayingInputCursor;
+public:
+ static Ref<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; }
+
+private:
+ ReplaySessionSegment();
+
+ std::unique_ptr<SegmentedInputStorage> m_storage;
+ Vector<double, 0> m_eventLoopTimings;
+
+ unsigned m_identifier;
+ bool m_canCapture {true};
+ double m_timestamp;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayingInputCursor.cpp b/Source/WebCore/replay/ReplayingInputCursor.cpp
new file mode 100644
index 000000000..0e26a2a4b
--- /dev/null
+++ b/Source/WebCore/replay/ReplayingInputCursor.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "ReplayingInputCursor.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "EventLoopInputDispatcher.h"
+#include "ReplaySessionSegment.h"
+#include "SegmentedInputStorage.h"
+#include "SerializationMethods.h"
+#include "WebReplayInputs.h"
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+ReplayingInputCursor::ReplayingInputCursor(RefPtr<ReplaySessionSegment>&& segment, Page& page, EventLoopInputDispatcherClient* client)
+ : m_segment(WTFMove(segment))
+ , m_dispatcher(std::make_unique<EventLoopInputDispatcher>(page, *this, client))
+{
+ for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++)
+ m_positions.append(0);
+}
+
+ReplayingInputCursor::~ReplayingInputCursor()
+{
+}
+
+Ref<ReplayingInputCursor> ReplayingInputCursor::create(RefPtr<ReplaySessionSegment>&& segment, Page& page, EventLoopInputDispatcherClient* client)
+{
+ return adoptRef(*new ReplayingInputCursor(WTFMove(segment), page, client));
+}
+
+void ReplayingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase>)
+{
+ // Cannot store inputs from a replaying input cursor.
+ ASSERT_NOT_REACHED();
+}
+
+NondeterministicInputBase* ReplayingInputCursor::loadInput(InputQueue queue, const String& type)
+{
+ NondeterministicInputBase* input = uncheckedLoadInput(queue);
+
+ if (input->type() != type) {
+ LOG_ERROR("%-25s ERROR: Expected replay input of type %s, but got type %s\n", "[ReplayingInputCursor]", type.ascii().data(), input->type().ascii().data());
+ return nullptr;
+ }
+
+ return input;
+}
+
+NondeterministicInputBase* ReplayingInputCursor::uncheckedLoadInput(InputQueue 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_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
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplayingInputCursor.h b/Source/WebCore/replay/ReplayingInputCursor.h
new file mode 100644
index 000000000..77c3b9420
--- /dev/null
+++ b/Source/WebCore/replay/ReplayingInputCursor.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <replay/InputCursor.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class EventLoopInputBase;
+class EventLoopInputDispatcher;
+class EventLoopInputDispatcherClient;
+class Page;
+class ReplaySessionSegment;
+
+struct EventLoopInputData {
+ EventLoopInputBase* input;
+ double timestamp;
+};
+
+class ReplayingInputCursor final : public InputCursor {
+ WTF_MAKE_NONCOPYABLE(ReplayingInputCursor);
+public:
+ static Ref<ReplayingInputCursor> create(RefPtr<ReplaySessionSegment>&&, Page&, EventLoopInputDispatcherClient*);
+ virtual ~ReplayingInputCursor();
+
+ bool isCapturing() const override { return false; }
+ bool isReplaying() const override { return true; }
+
+ EventLoopInputDispatcher& dispatcher() const { return *m_dispatcher; }
+
+ EventLoopInputData loadEventLoopInput();
+protected:
+ NondeterministicInputBase* loadInput(InputQueue, const String& type) override;
+private:
+ ReplayingInputCursor(RefPtr<ReplaySessionSegment>&&, Page&, EventLoopInputDispatcherClient*);
+
+ void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
+ NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+
+ RefPtr<ReplaySessionSegment> m_segment;
+ std::unique_ptr<EventLoopInputDispatcher> m_dispatcher;
+ Vector<size_t> m_positions;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/SegmentedInputStorage.cpp b/Source/WebCore/replay/SegmentedInputStorage.cpp
new file mode 100644
index 000000000..e61b069e5
--- /dev/null
+++ b/Source/WebCore/replay/SegmentedInputStorage.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "SegmentedInputStorage.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#if !LOG_DISABLED
+#include "Logging.h"
+#include "SerializationMethods.h"
+#include <replay/EncodedValue.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+#endif
+
+namespace WebCore {
+
+#if !LOG_DISABLED
+// These are used to make the log spew from LOG(WebReplay, ...) more readable.
+static const char* queueTypeToLogPrefix(InputQueue inputQueue, bool isLoad)
+{
+ if (isLoad) {
+ switch (inputQueue) {
+ case InputQueue::EventLoopInput: return "(DSPTCH-LOAD)";
+ case InputQueue::LoaderMemoizedData: return "<LDMEMO-LOAD";
+ case InputQueue::ScriptMemoizedData: return "<---<---<---JSMEMO-LOAD";
+ case InputQueue::Count: return "ERROR!";
+ }
+ } else {
+ switch (inputQueue) {
+ case InputQueue::EventLoopInput: return ">DSPTCH-STORE";
+ case InputQueue::LoaderMemoizedData: return "<LDMEMO-STORE";
+ case InputQueue::ScriptMemoizedData: return "<---<---<---JSMEMO-STORE";
+ case InputQueue::Count: return "ERROR!";
+ }
+ }
+}
+
+static String jsonStringForInput(const NondeterministicInputBase& input)
+{
+ EncodedValue encodedValue = EncodingTraits<NondeterministicInputBase>::encodeValue(input);
+ return encodedValue.asObject()->toJSONString();
+}
+#endif // !LOG_DISABLED
+
+static size_t offsetForInputQueue(InputQueue inputQueue)
+{
+ return static_cast<size_t>(inputQueue);
+}
+
+SegmentedInputStorage::SegmentedInputStorage()
+{
+ for (size_t i = 0; i < offsetForInputQueue(InputQueue::Count); i++)
+ m_queues.append(new QueuedInputs);
+}
+
+SegmentedInputStorage::~SegmentedInputStorage()
+{
+ for (size_t i = 0; i < offsetForInputQueue(InputQueue::Count); i++)
+ delete m_queues.at(i);
+}
+
+NondeterministicInputBase* SegmentedInputStorage::load(InputQueue inputQueue, size_t offset)
+{
+ ASSERT(offset < queueSize(inputQueue));
+
+ NondeterministicInputBase* input = queue(inputQueue).at(offset).get();
+ ASSERT(input);
+
+ LOG(WebReplay, "%-20s %s: %s %s\n", "ReplayEvents", queueTypeToLogPrefix(inputQueue, true), input->type().utf8().data(), jsonStringForInput(*input).utf8().data());
+
+ return input;
+}
+
+void SegmentedInputStorage::store(std::unique_ptr<NondeterministicInputBase> input)
+{
+ ASSERT_ARG(input, input);
+ ASSERT_ARG(input, input->queue() < InputQueue::Count);
+
+ LOG(WebReplay, "%-14s#%-5u %s: %s %s\n", "ReplayEvents", m_inputCount++, queueTypeToLogPrefix(input->queue(), false), input->type().utf8().data(), jsonStringForInput(*input).utf8().data());
+
+ m_queues.at(offsetForInputQueue(input->queue()))->append(WTFMove(input));
+}
+
+size_t SegmentedInputStorage::queueSize(InputQueue inputQueue) const
+{
+ return queue(inputQueue).size();
+}
+
+const SegmentedInputStorage::QueuedInputs& SegmentedInputStorage::queue(InputQueue queue) const
+{
+ ASSERT_ARG(queue, queue < InputQueue::Count);
+ return *m_queues.at(offsetForInputQueue(queue));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/SegmentedInputStorage.h b/Source/WebCore/replay/SegmentedInputStorage.h
new file mode 100644
index 000000000..5294562f0
--- /dev/null
+++ b/Source/WebCore/replay/SegmentedInputStorage.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <replay/NondeterministicInput.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class SegmentedInputStorage {
+ WTF_MAKE_NONCOPYABLE(SegmentedInputStorage);
+ friend class FunctorInputCursor;
+public:
+ SegmentedInputStorage();
+ ~SegmentedInputStorage();
+
+ NondeterministicInputBase* load(InputQueue, size_t);
+ void store(std::unique_ptr<NondeterministicInputBase>);
+ size_t queueSize(InputQueue) const;
+
+private:
+ typedef Vector<std::unique_ptr<NondeterministicInputBase>> QueuedInputs;
+ const QueuedInputs& queue(InputQueue) const;
+
+ Vector<QueuedInputs*, 3> m_queues;
+ unsigned m_inputCount {0};
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/SerializationMethods.cpp b/Source/WebCore/replay/SerializationMethods.cpp
new file mode 100644
index 000000000..ca017fbd8
--- /dev/null
+++ b/Source/WebCore/replay/SerializationMethods.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2012 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "SerializationMethods.h"
+
+#if ENABLE(WEB_REPLAY)
+
+#include "AllReplayInputs.h"
+#include "Document.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "MainFrame.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformWheelEvent.h"
+#include "PluginData.h"
+#include "SecurityOrigin.h"
+#include "URL.h"
+#include <wtf/text/Base64.h>
+
+using WebCore::IntPoint;
+using WebCore::MimeClassInfo;
+using WebCore::MouseButton;
+using WebCore::PlatformEvent;
+using WebCore::PlatformKeyboardEvent;
+using WebCore::PlatformMouseEvent;
+using WebCore::PlatformWheelEvent;
+using WebCore::PlatformWheelEventGranularity;
+using WebCore::PluginData;
+using WebCore::PluginLoadClientPolicy;
+using WebCore::PluginInfo;
+using WebCore::SecurityOrigin;
+using WebCore::URL;
+
+#if PLATFORM(COCOA)
+using WebCore::KeypressCommand;
+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
+
+namespace WebCore {
+
+uint32_t frameIndexFromDocument(const Document* document)
+{
+ ASSERT(document);
+ ASSERT(document->frame());
+ return frameIndexFromFrame(document->frame());
+}
+
+uint32_t frameIndexFromFrame(const Frame* targetFrame)
+{
+ ASSERT(targetFrame);
+
+ uint32_t currentIndex = 0;
+ for (const Frame* frame = &targetFrame->tree().top(); frame; ++currentIndex, frame = frame->tree().traverseNext()) {
+ if (frame == targetFrame)
+ return currentIndex;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+Document* documentFromFrameIndex(Page* page, uint32_t frameIndex)
+{
+ Frame* frame = frameFromFrameIndex(page, frameIndex);
+ return frame ? frame->document() : nullptr;
+}
+
+Frame* frameFromFrameIndex(Page* page, uint32_t frameIndex)
+{
+ ASSERT(page);
+ ASSERT(frameIndex >= 0);
+
+ Frame* frame = &page->mainFrame();
+ uint32_t currentIndex = 0;
+ for (; currentIndex < frameIndex && frame; ++currentIndex, frame = frame->tree().traverseNext()) { }
+
+ return frame;
+}
+
+} // namespace WebCore
+
+#define ENCODE_TYPE_WITH_KEY(_encodedValue, _type, _key, _value) \
+ _encodedValue.put<_type>(ASCIILiteral(#_key), _value)
+
+#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 DECODE_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+ EncodingTraits<_type>::DecodedType _key; \
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
+
+#define DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _lvalue) \
+ bool _key ## WasDecoded = _encodedValue.get<_type>(ASCIILiteral(#_key), _lvalue)
+
+#define DECODE_OPTIONAL_TYPE_WITH_KEY(_encodedValue, _type, _key) \
+ EncodingTraits<_type>::DecodedType _key; \
+ DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)
+
+namespace JSC {
+
+template<>
+EncodedValue EncodingTraits<MimeClassInfo>::encodeValue(const MimeClassInfo& input)
+{
+ EncodedValue encodedData = EncodedValue::createObject();
+
+ ENCODE_TYPE_WITH_KEY(encodedData, String, type, input.type);
+ ENCODE_TYPE_WITH_KEY(encodedData, String, desc, input.desc);
+ ENCODE_TYPE_WITH_KEY(encodedData, Vector<String>, extensions, input.extensions);
+
+ return encodedData;
+}
+
+template<>
+bool EncodingTraits<MimeClassInfo>::decodeValue(EncodedValue& encodedData, MimeClassInfo& input)
+{
+ MimeClassInfo info;
+
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, type, info.type);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, desc, info.desc);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, Vector<String>, extensions, info.extensions);
+
+ input = info;
+ return true;
+}
+
+EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const NondeterministicInputBase& input)
+{
+ EncodedValue encodedValue = EncodedValue::createObject();
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, type, input.type());
+
+#define ENCODE_IF_TYPE_TAG_MATCHES(name) \
+ if (is<name>(input)) { \
+ InputTraits<name>::encode(encodedValue, downcast<name>(input)); \
+ return encodedValue; \
+ } \
+
+ JS_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES)
+ WEB_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES)
+#undef ENCODE_IF_TYPE_TAG_MATCHES
+
+ // The macro won't work here because of the class template argument.
+ if (is<MemoizedDOMResultBase>(input)) {
+ InputTraits<MemoizedDOMResultBase>::encode(encodedValue, downcast<MemoizedDOMResultBase>(input));
+ return encodedValue;
+ }
+
+ ASSERT_NOT_REACHED();
+ return EncodedValue();
+}
+
+bool EncodingTraits<NondeterministicInputBase>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<NondeterministicInputBase>& input)
+{
+ DECODE_TYPE_WITH_KEY(encodedValue, String, type);
+
+#define DECODE_IF_TYPE_TAG_MATCHES(name) \
+ if (type == InputTraits<name>::type()) { \
+ std::unique_ptr<name> decodedInput; \
+ if (!InputTraits<name>::decode(encodedValue, decodedInput)) \
+ return false; \
+ \
+ input = WTFMove(decodedInput); \
+ return true; \
+ } \
+
+ JS_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES)
+ WEB_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES)
+#undef DECODE_IF_TYPE_TAG_MATCHES
+
+ if (type == InputTraits<MemoizedDOMResultBase>::type()) {
+ std::unique_ptr<MemoizedDOMResultBase> decodedInput;
+ if (!InputTraits<MemoizedDOMResultBase>::decode(encodedValue, decodedInput))
+ return false;
+
+ input = WTFMove(decodedInput);
+ return true;
+ }
+
+ return false;
+}
+
+#if USE(APPKIT)
+EncodedValue EncodingTraits<KeypressCommand>::encodeValue(const KeypressCommand& command)
+{
+ EncodedValue encodedValue = EncodedValue::createObject();
+
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, commandName, command.commandName);
+ ENCODE_OPTIONAL_TYPE_WITH_KEY(encodedValue, String, text, command.text, !command.text.isEmpty());
+
+ return encodedValue;
+}
+
+bool EncodingTraits<KeypressCommand>::decodeValue(EncodedValue& encodedValue, KeypressCommand& decodedValue)
+{
+ DECODE_TYPE_WITH_KEY(encodedValue, String, commandName);
+ DECODE_OPTIONAL_TYPE_WITH_KEY(encodedValue, String, text);
+
+ decodedValue = textWasDecoded ? KeypressCommand(commandName, text) : KeypressCommand(commandName);
+ return true;
+}
+
+class PlatformKeyboardEventAppKit : public WebCore::PlatformKeyboardEvent {
+public:
+ PlatformKeyboardEventAppKit(const PlatformKeyboardEvent& event, bool handledByInputMethod, Vector<KeypressCommand>& commands)
+ : PlatformKeyboardEvent(event)
+ {
+ m_handledByInputMethod = handledByInputMethod;
+ m_commands = commands;
+ }
+};
+#endif // USE(APPKIT)
+
+EncodedValue EncodingTraits<PlatformKeyboardEvent>::encodeValue(const PlatformKeyboardEvent& input)
+{
+ EncodedValue encodedValue = EncodedValue::createObject();
+
+ 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::Modifier, modifiers, input.modifiers());
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, text, input.text());
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, unmodifiedText, input.unmodifiedText());
+#if ENABLE(KEYBOARD_KEY_ATTRIBUTE)
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, key, input.key());
+#endif
+#if ENABLE(KEYBOARD_CODE_ATTRIBUTE)
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, code, input.code());
+#endif
+ ENCODE_TYPE_WITH_KEY(encodedValue, String, keyIdentifier, input.keyIdentifier());
+ ENCODE_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode, input.windowsVirtualKeyCode());
+ 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_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod, input.handledByInputMethod());
+ ENCODE_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands, input.commands());
+#endif
+ return encodedValue;
+}
+
+bool EncodingTraits<PlatformKeyboardEvent>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<PlatformKeyboardEvent>& input)
+{
+ DECODE_TYPE_WITH_KEY(encodedValue, double, timestamp);
+ DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
+ DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifier, modifiers);
+ DECODE_TYPE_WITH_KEY(encodedValue, String, text);
+ DECODE_TYPE_WITH_KEY(encodedValue, String, unmodifiedText);
+#if ENABLE(KEYBOARD_KEY_ATTRIBUTE)
+ DECODE_TYPE_WITH_KEY(encodedValue, String, key);
+#endif
+#if ENABLE(KEYBOARD_CODE_ATTRIBUTE)
+ DECODE_TYPE_WITH_KEY(encodedValue, String, code);
+#endif
+ DECODE_TYPE_WITH_KEY(encodedValue, String, keyIdentifier);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, autoRepeat);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, keypad);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, systemKey);
+#if USE(APPKIT)
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod);
+ DECODE_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands);
+#endif
+
+ PlatformKeyboardEvent platformEvent = PlatformKeyboardEvent(type, text, unmodifiedText,
+#if ENABLE(KEYBOARD_KEY_ATTRIBUTE)
+ key,
+#endif
+#if ENABLE(KEYBOARD_CODE_ATTRIBUTE)
+ code,
+#endif
+ keyIdentifier, WTF::safeCast<int>(windowsVirtualKeyCode), autoRepeat, keypad, systemKey, modifiers, timestamp);
+
+#if USE(APPKIT)
+ input = std::make_unique<PlatformKeyboardEventAppKit>(platformEvent, handledByInputMethod, commands);
+#else
+ input = std::make_unique<PlatformKeyboardEvent>(platformEvent);
+#endif
+ return true;
+}
+
+EncodedValue EncodingTraits<PlatformMouseEvent>::encodeValue(const PlatformMouseEvent& input)
+{
+ EncodedValue encodedValue = EncodedValue::createObject();
+
+ 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());
+ ENCODE_TYPE_WITH_KEY(encodedValue, double, force, input.force());
+
+ return encodedValue;
+}
+
+bool EncodingTraits<PlatformMouseEvent>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<PlatformMouseEvent>& input)
+{
+ DECODE_TYPE_WITH_KEY(encodedValue, int, positionX);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, positionY);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, globalPositionX);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, globalPositionY);
+ DECODE_TYPE_WITH_KEY(encodedValue, MouseButton, button);
+ DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, clickCount);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, shiftKey);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, ctrlKey);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, altKey);
+ DECODE_TYPE_WITH_KEY(encodedValue, bool, metaKey);
+ DECODE_TYPE_WITH_KEY(encodedValue, int, timestamp);
+ DECODE_TYPE_WITH_KEY(encodedValue, double, force);
+
+ input = std::make_unique<PlatformMouseEvent>(IntPoint(positionX, positionY),
+ IntPoint(globalPositionX, globalPositionY),
+ button, type, clickCount,
+ shiftKey, ctrlKey, altKey, metaKey, timestamp, force, WebCore::NoTap);
+ 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_TYPE_WITH_KEY(encodedData, int, positionX);
+ DECODE_TYPE_WITH_KEY(encodedData, int, positionY);
+ DECODE_TYPE_WITH_KEY(encodedData, int, globalPositionX);
+ DECODE_TYPE_WITH_KEY(encodedData, int, globalPositionY);
+ DECODE_TYPE_WITH_KEY(encodedData, bool, shiftKey);
+ DECODE_TYPE_WITH_KEY(encodedData, bool, ctrlKey);
+ DECODE_TYPE_WITH_KEY(encodedData, bool, altKey);
+ DECODE_TYPE_WITH_KEY(encodedData, bool, metaKey);
+ DECODE_TYPE_WITH_KEY(encodedData, float, deltaX);
+ DECODE_TYPE_WITH_KEY(encodedData, float, deltaY);
+ DECODE_TYPE_WITH_KEY(encodedData, float, wheelTicksX);
+ DECODE_TYPE_WITH_KEY(encodedData, float, wheelTicksY);
+ DECODE_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<PluginData>::encodeValue(RefPtr<PluginData> input)
+{
+ // FIXME: This needs to work in terms of web-visible plug-ins.
+ EncodedValue encodedData = EncodedValue::createObject();
+
+ ENCODE_TYPE_WITH_KEY(encodedData, Vector<PluginInfo>, plugins, input->plugins());
+
+ return encodedData;
+}
+
+bool EncodingTraits<PluginData>::decodeValue(EncodedValue& encodedData, RefPtr<PluginData>&)
+{
+ // FIXME: This needs to work in terms of web-visible plug-ins.
+ DECODE_TYPE_WITH_KEY(encodedData, Vector<PluginInfo>, plugins);
+
+ return true;
+}
+
+template<>
+EncodedValue EncodingTraits<PluginInfo>::encodeValue(const PluginInfo& input)
+{
+ EncodedValue encodedData = EncodedValue::createObject();
+
+ ENCODE_TYPE_WITH_KEY(encodedData, String, name, input.name);
+ ENCODE_TYPE_WITH_KEY(encodedData, String, file, input.file);
+ ENCODE_TYPE_WITH_KEY(encodedData, String, desc, input.desc);
+ ENCODE_TYPE_WITH_KEY(encodedData, Vector<MimeClassInfo>, mimes, input.mimes);
+ ENCODE_TYPE_WITH_KEY(encodedData, bool, isApplicationPlugin, input.isApplicationPlugin);
+ ENCODE_TYPE_WITH_KEY(encodedData, PluginLoadClientPolicy, clientLoadPolicy, static_cast<PluginLoadClientPolicy>(input.clientLoadPolicy));
+#if PLATFORM(MAC)
+ ENCODE_TYPE_WITH_KEY(encodedData, String, bundleIdentifier, input.bundleIdentifier);
+ ENCODE_TYPE_WITH_KEY(encodedData, String, versionString, input.versionString);
+#endif
+
+ return encodedData;
+}
+
+template<>
+bool EncodingTraits<PluginInfo>::decodeValue(EncodedValue& encodedData, PluginInfo& input)
+{
+ PluginInfo info;
+
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, name, info.name);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, file, info.file);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, desc, info.desc);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, Vector<MimeClassInfo>, mimes, info.mimes);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, isApplicationPlugin, info.isApplicationPlugin);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PluginLoadClientPolicy, clientLoadPolicy, info.clientLoadPolicy);
+#if PLATFORM(MAC)
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, bundleIdentifier, input.bundleIdentifier);
+ DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, versionString, input.versionString);
+#endif
+
+ input = info;
+ return true;
+}
+
+EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input)
+{
+ return EncodedValue::createString(input->toString());
+}
+
+bool EncodingTraits<SecurityOrigin>::decodeValue(EncodedValue& encodedValue, RefPtr<SecurityOrigin>& input)
+{
+ input = SecurityOrigin::createFromString(encodedValue.convertTo<String>());
+ return true;
+}
+
+EncodedValue EncodingTraits<URL>::encodeValue(const URL& input)
+{
+ return EncodedValue::createString(input.string());
+}
+
+bool EncodingTraits<URL>::decodeValue(EncodedValue& encodedValue, URL& input)
+{
+ input = URL(WebCore::ParsedURLString, encodedValue.convertTo<String>());
+ return true;
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/SerializationMethods.h b/Source/WebCore/replay/SerializationMethods.h
new file mode 100644
index 000000000..7d1b67812
--- /dev/null
+++ b/Source/WebCore/replay/SerializationMethods.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_REPLAY)
+
+#include <replay/EncodedValue.h>
+#include <replay/NondeterministicInput.h>
+
+namespace WebCore {
+
+class Document;
+class Frame;
+class Page;
+class PlatformKeyboardEvent;
+class PlatformMouseEvent;
+class PlatformWheelEvent;
+class PluginData;
+class SecurityOrigin;
+class URL;
+
+#if USE(APPKIT)
+struct KeypressCommand;
+#endif
+
+uint32_t frameIndexFromDocument(const Document*);
+uint32_t frameIndexFromFrame(const Frame*);
+Document* documentFromFrameIndex(Page*, uint32_t frameIndex);
+Frame* frameFromFrameIndex(Page*, uint32_t frameIndex);
+
+} // namespace WebCore
+
+// Template specializations must be defined in the same namespace as the template declaration.
+namespace JSC {
+
+#if USE(APPKIT)
+template<> struct EncodingTraits<WebCore::KeypressCommand> {
+ typedef WebCore::KeypressCommand DecodedType;
+
+ static EncodedValue encodeValue(const WebCore::KeypressCommand& value);
+ static bool decodeValue(EncodedValue&, WebCore::KeypressCommand& value);
+};
+#endif // USE(APPKIT)
+
+template<> struct EncodingTraits<NondeterministicInputBase> {
+ typedef NondeterministicInputBase DecodedType;
+
+ static EncodedValue encodeValue(const NondeterministicInputBase& value);
+ static bool decodeValue(EncodedValue&, std::unique_ptr<NondeterministicInputBase>& value);
+};
+
+template<> struct EncodingTraits<WebCore::PlatformKeyboardEvent> {
+ typedef WebCore::PlatformKeyboardEvent DecodedType;
+
+ static EncodedValue encodeValue(const WebCore::PlatformKeyboardEvent& value);
+ static bool decodeValue(EncodedValue&, std::unique_ptr<WebCore::PlatformKeyboardEvent>& value);
+};
+
+template<> struct EncodingTraits<WebCore::PlatformMouseEvent> {
+ typedef WebCore::PlatformMouseEvent DecodedType;
+
+ static EncodedValue encodeValue(const WebCore::PlatformMouseEvent& value);
+ static bool decodeValue(EncodedValue&, std::unique_ptr<WebCore::PlatformMouseEvent>& value);
+};
+
+template<> struct EncodingTraits<WebCore::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::PluginData> {
+ typedef RefPtr<WebCore::PluginData> DecodedType;
+
+ static EncodedValue encodeValue(RefPtr<WebCore::PluginData> value);
+ static bool decodeValue(EncodedValue&, RefPtr<WebCore::PluginData>& value);
+};
+
+template<> struct EncodingTraits<WebCore::URL> {
+ typedef WebCore::URL DecodedType;
+
+ static EncodedValue encodeValue(const WebCore::URL& value);
+ static bool decodeValue(EncodedValue&, WebCore::URL& value);
+};
+
+template<> struct EncodingTraits<WebCore::SecurityOrigin> {
+ typedef RefPtr<WebCore::SecurityOrigin> DecodedType;
+
+ static EncodedValue encodeValue(RefPtr<WebCore::SecurityOrigin> value);
+ static bool decodeValue(EncodedValue&, RefPtr<WebCore::SecurityOrigin>& value);
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/UserInputBridge.cpp b/Source/WebCore/replay/UserInputBridge.cpp
new file mode 100644
index 000000000..233bcc475
--- /dev/null
+++ b/Source/WebCore/replay/UserInputBridge.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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 "UserInputBridge.h"
+
+#include "EventHandler.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoadRequest.h"
+#include "MainFrame.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformWheelEvent.h"
+
+#if ENABLE(WEB_REPLAY)
+#include "ReplayController.h"
+#include "SerializationMethods.h"
+#include "WebReplayInputs.h"
+#include <replay/InputCursor.h>
+#endif
+
+#define EARLY_RETURN_IF_SHOULD_IGNORE_INPUT \
+ do { \
+ if (inputSource == InputSource::User && m_state == UserInputBridge::State::Replaying) \
+ return true; \
+ } while (false)
+
+namespace WebCore {
+
+UserInputBridge::UserInputBridge(Page& page)
+ : m_page(page)
+#if ENABLE(WEB_REPLAY)
+ , m_state(UserInputBridge::State::Open)
+#endif
+{
+}
+
+#if ENABLE(WEB_REPLAY)
+InputCursor& UserInputBridge::activeCursor() const
+{
+ return m_page.replayController().activeInputCursor();
+}
+#endif
+
+#if ENABLE(CONTEXT_MENUS)
+bool UserInputBridge::handleContextMenuEvent(const PlatformMouseEvent& mouseEvent, const Frame* frame, InputSource)
+{
+ return frame->eventHandler().sendContextMenuEvent(mouseEvent);
+}
+#endif
+
+bool UserInputBridge::handleMousePressEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+ cursor.appendInput<HandleMousePress>(WTFMove(ownedEvent));
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.mainFrame().eventHandler().handleMousePressEvent(mouseEvent);
+}
+
+bool UserInputBridge::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+ cursor.appendInput<HandleMouseRelease>(WTFMove(ownedEvent));
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.mainFrame().eventHandler().handleMouseReleaseEvent(mouseEvent);
+}
+
+bool UserInputBridge::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+ cursor.appendInput<HandleMouseMove>(WTFMove(ownedEvent), false);
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.mainFrame().eventHandler().mouseMoved(mouseEvent);
+}
+
+bool UserInputBridge::handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent& mouseEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformMouseEvent> ownedEvent = std::make_unique<PlatformMouseEvent>(mouseEvent);
+ cursor.appendInput<HandleMouseMove>(WTFMove(ownedEvent), true);
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.mainFrame().eventHandler().passMouseMovedEventToScrollbars(mouseEvent);
+}
+
+bool UserInputBridge::handleMouseForceEvent(const PlatformMouseEvent& mouseEvent, InputSource)
+{
+ return m_page.mainFrame().eventHandler().handleMouseForceEvent(mouseEvent);
+}
+
+bool UserInputBridge::handleKeyEvent(const PlatformKeyboardEvent& keyEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformKeyboardEvent> ownedEvent = std::make_unique<PlatformKeyboardEvent>(keyEvent);
+ cursor.appendInput<HandleKeyPress>(WTFMove(ownedEvent));
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.focusController().focusedOrMainFrame().eventHandler().keyEvent(keyEvent);
+}
+
+bool UserInputBridge::handleAccessKeyEvent(const PlatformKeyboardEvent& keyEvent, InputSource)
+{
+ return m_page.focusController().focusedOrMainFrame().eventHandler().handleAccessKey(keyEvent);
+}
+
+bool UserInputBridge::handleWheelEvent(const PlatformWheelEvent& wheelEvent, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing()) {
+ std::unique_ptr<PlatformWheelEvent> ownedEvent = std::make_unique<PlatformWheelEvent>(wheelEvent);
+ cursor.appendInput<HandleWheelEvent>(WTFMove(ownedEvent));
+ }
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.mainFrame().eventHandler().handleWheelEvent(wheelEvent);
+}
+
+void UserInputBridge::focusSetActive(bool active, InputSource)
+{
+ m_page.focusController().setActive(active);
+}
+
+void UserInputBridge::focusSetFocused(bool focused, InputSource)
+{
+ m_page.focusController().setFocused(focused);
+}
+
+bool UserInputBridge::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing())
+ cursor.appendInput<ScrollPage>(direction, granularity);
+
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.focusController().focusedOrMainFrame().eventHandler().scrollRecursively(direction, granularity, nullptr);
+}
+
+bool UserInputBridge::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, InputSource inputSource)
+{
+#if ENABLE(WEB_REPLAY)
+ EARLY_RETURN_IF_SHOULD_IGNORE_INPUT;
+
+ InputCursor& cursor = activeCursor();
+ if (cursor.isCapturing())
+ cursor.appendInput<LogicalScrollPage>(direction, granularity);
+
+ EventLoopInputExtent extent(cursor);
+#else
+ UNUSED_PARAM(inputSource);
+#endif
+
+ return m_page.focusController().focusedOrMainFrame().eventHandler().logicalScrollRecursively(direction, granularity, nullptr);
+}
+
+void UserInputBridge::loadRequest(const FrameLoadRequest& request, InputSource)
+{
+ m_page.mainFrame().loader().load(request);
+}
+
+void UserInputBridge::reloadFrame(Frame* frame, bool endToEndReload, bool contentBlockersEnabled, InputSource)
+{
+ frame->loader().reload(endToEndReload, contentBlockersEnabled);
+}
+
+void UserInputBridge::stopLoadingFrame(Frame* frame, InputSource)
+{
+ frame->loader().stopForUserCancel();
+}
+
+bool UserInputBridge::tryClosePage(InputSource)
+{
+ return m_page.mainFrame().loader().shouldClose();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/replay/UserInputBridge.h b/Source/WebCore/replay/UserInputBridge.h
new file mode 100644
index 000000000..9746211d7
--- /dev/null
+++ b/Source/WebCore/replay/UserInputBridge.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012, 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * HOLDER OR 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.
+ */
+
+#pragma once
+
+#include "ScrollTypes.h"
+#include <wtf/Noncopyable.h>
+
+namespace JSC {
+class InputCursor;
+}
+
+namespace WebCore {
+
+struct FrameLoadRequest;
+
+class Frame;
+class Page;
+class PlatformKeyboardEvent;
+class PlatformMouseEvent;
+class PlatformWheelEvent;
+
+// Real user inputs come from WebKit or WebKit2.
+// Synthetic inputs come from within WebCore (i.e., from web replay or fake mouse moves).
+enum class InputSource {
+ User,
+ Synthetic
+};
+
+class UserInputBridge {
+ WTF_MAKE_NONCOPYABLE(UserInputBridge);
+public:
+ UserInputBridge(Page&);
+
+#if ENABLE(WEB_REPLAY)
+ enum class State {
+ Capturing,
+ Open,
+ Replaying,
+ };
+
+ void setState(State bridgeState) { m_state = bridgeState; }
+ State state() const { return m_state; }
+
+ JSC::InputCursor& activeCursor() const;
+#endif
+
+ // User input APIs.
+#if ENABLE(CONTEXT_MENUS)
+ WEBCORE_EXPORT bool handleContextMenuEvent(const PlatformMouseEvent&, const Frame*, InputSource source = InputSource::User);
+#endif
+ WEBCORE_EXPORT bool handleMousePressEvent(const PlatformMouseEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleMouseReleaseEvent(const PlatformMouseEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleMouseMoveEvent(const PlatformMouseEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleMouseMoveOnScrollbarEvent(const PlatformMouseEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleMouseForceEvent(const PlatformMouseEvent&, InputSource = InputSource::User);
+ WEBCORE_EXPORT bool handleWheelEvent(const PlatformWheelEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleKeyEvent(const PlatformKeyboardEvent&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool handleAccessKeyEvent(const PlatformKeyboardEvent&, InputSource source = InputSource::User);
+ void focusSetActive(bool active, InputSource source = InputSource::User);
+ void focusSetFocused(bool focused, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool scrollRecursively(ScrollDirection, ScrollGranularity, InputSource source = InputSource::User);
+ bool logicalScrollRecursively(ScrollLogicalDirection, ScrollGranularity, InputSource source = InputSource::User);
+
+ // Navigation APIs.
+ WEBCORE_EXPORT void loadRequest(const FrameLoadRequest&, InputSource source = InputSource::User);
+ WEBCORE_EXPORT void reloadFrame(Frame*, bool endToEndReload, bool contentBlockersEnabled, InputSource = InputSource::User);
+ WEBCORE_EXPORT void stopLoadingFrame(Frame*, InputSource source = InputSource::User);
+ WEBCORE_EXPORT bool tryClosePage(InputSource source = InputSource::User);
+
+private:
+ Page& m_page;
+#if ENABLE(WEB_REPLAY)
+ State m_state;
+#endif
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/replay/WebInputs.json b/Source/WebCore/replay/WebInputs.json
new file mode 100644
index 000000000..b7e5bcd83
--- /dev/null
+++ b/Source/WebCore/replay/WebInputs.json
@@ -0,0 +1,244 @@
+{
+ "types": {
+ "WebCore": [
+ {
+ "name": "EncodedCType", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM_CLASS"],
+ "values": [
+ "Boolean",
+ "Int",
+ "String",
+ "Unsigned"
+ ],
+ "header": "replay/MemoizedDOMResult.h"
+ },
+ {
+ "name": "Modifier", "mode": "SCALAR",
+ "enclosing_class": "PlatformEvent",
+ "flags": ["OPTION_SET"],
+ "values": ["AltKey", "CtrlKey", "MetaKey", "ShiftKey"],
+ "header": "platform/PlatformEvent.h"
+ },
+ {
+ "name": "MouseButton", "mode": "SCALAR", "storage": "int8_t",
+ "flags": ["ENUM"],
+ "values": ["NoButton", "LeftButton", "MiddleButton", "RightButton"],
+ "header": "platform/PlatformMouseEvent.h"
+ },
+ {
+ "name": "Page", "mode": "OWNED",
+ "header": "page/Page.h"
+ },
+ {
+ "name": "PlatformKeyboardEvent", "mode": "OWNED",
+ "header": "platform/PlatformKeyboardEvent.h"
+ },
+ {
+ "name": "PlatformMouseEvent", "mode": "OWNED",
+ "header": "platform/PlatformMouseEvent.h"
+ },
+ {
+ "name": "PlatformWheelEvent", "mode": "OWNED",
+ "header": "platform/PlatformWheelEvent.h"
+ },
+ {
+ "name": "PlatformWheelEventGranularity", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "values": ["ScrollByPageWheelEvent", "ScrollByPixelWheelEvent"],
+ "header": "platform/PlatformWheelEvent.h"
+ },
+ {
+ "name": "PlatformWheelEventPhase", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "guard": "PLATFORM(COCOA)",
+ "values": [
+ "PlatformWheelEventPhaseNone",
+ "PlatformWheelEventPhaseBegan",
+ "PlatformWheelEventPhaseStationary",
+ "PlatformWheelEventPhaseChanged",
+ "PlatformWheelEventPhaseEnded",
+ "PlatformWheelEventPhaseCancelled",
+ "PlatformWheelEventPhaseMayBegin"
+ ],
+ "header": "platform/PlatformWheelEvent.h"
+ },
+ {
+ "name": "PluginData", "mode": "SHARED",
+ "header": "plugins/PluginData.h"
+ },
+ {
+ "name": "PluginLoadClientPolicy", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "values": ["PluginLoadClientPolicyUndefined", "PluginLoadClientPolicyBlock", "PluginLoadClientPolicyAsk", "PluginLoadClientPolicyAllow", "PluginLoadClientPolicyAllowAlways"],
+ "header": "plugins/PluginData.h"
+ },
+
+ {
+ "name": "ScrollDirection", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "values": ["ScrollUp", "ScrollDown", "ScrollLeft", "ScrollRight"],
+ "header": "platform/ScrollTypes.h"
+ },
+ {
+ "name": "ScrollGranularity", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "values": [
+ "ScrollByLine",
+ "ScrollByPage",
+ "ScrollByDocument",
+ "ScrollByPixel",
+ "ScrollByPrecisePixel"
+ ],
+ "header": "platform/ScrollTypes.h"
+ },
+ {
+ "name": "ScrollLogicalDirection", "mode": "SCALAR", "storage": "uint8_t",
+ "flags": ["ENUM"],
+ "values": [
+ "ScrollBlockDirectionBackward",
+ "ScrollBlockDirectionForward",
+ "ScrollInlineDirectionBackward",
+ "ScrollInlineDirectionForward"
+ ],
+ "header": "platform/ScrollTypes.h"
+ },
+ {
+ "name": "SecurityOrigin", "mode": "SHARED",
+ "header": "page/SecurityOrigin.h"
+ },
+ {
+ "name": "Type", "mode": "SCALAR", "storage": "uint8_t",
+ "enclosing_class": "PlatformEvent",
+ "flags": ["ENUM"],
+ "values": [
+ "NoType",
+ "KeyDown",
+ "KeyUp",
+ "RawKeyDown",
+ "Char",
+ "MouseMoved",
+ "MousePressed",
+ "MouseReleased",
+ "MouseScroll",
+ "Wheel"
+ ],
+ "guarded_values": {
+ "ENABLE(TOUCH_EVENTS)": [
+ "TouchStart",
+ "TouchMove",
+ "TouchEnd",
+ "TouchCancel"
+ ]
+ },
+ "header": "platform/PlatformEvent.h"
+ },
+ {
+ "name": "URL", "mode": "HEAVY_SCALAR",
+ "header": "platform/URL.h"
+ }
+ ]
+ },
+
+ "inputs": {
+ "WebCore": [
+ {
+ "name": "BeginSegmentSentinel",
+ "description": "A sentinel input to signal the start of a segment.",
+ "queue": "EVENT_LOOP",
+ "members": [ ]
+ },
+ {
+ "name": "DocumentLastModifiedDate",
+ "description": "A fallback value used for the document's last modified date if the Last-Modified header can't be found or used.",
+ "queue": "SCRIPT_MEMOIZED",
+ "members": [
+ { "name": "fallbackValue", "type": "double" }
+ ]
+ },
+ {
+ "name": "EndSegmentSentinel",
+ "description": "A sentinel input to signal the end of a segment.",
+ "queue": "EVENT_LOOP",
+ "members": [ ]
+ },
+ {
+ "name": "HandleMouseMove",
+ "description": "The embedder signalled a mouse move event.",
+ "queue": "EVENT_LOOP",
+ "members": [
+ { "name": "platformEvent", "type": "PlatformMouseEvent" },
+ { "name": "scrollbarTargeted", "type": "bool" }
+ ]
+ },
+ {
+ "name": "HandleMousePress",
+ "description": "The embedder signalled a mouse press event.",
+ "queue": "EVENT_LOOP",
+ "members": [
+ { "name": "platformEvent", "type": "PlatformMouseEvent" }
+ ]
+ },
+ {
+ "name": "HandleMouseRelease",
+ "description": "The embedder signalled a mouse release event.",
+ "queue": "EVENT_LOOP",
+ "members": [
+ { "name": "platformEvent", "type": "PlatformMouseEvent" }
+ ]
+ },
+ {
+ "name": "HandleKeyPress",
+ "description": "The embedder signalled a key press event.",
+ "queue": "EVENT_LOOP",
+ "members": [
+ { "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.",
+ "queue": "EVENT_LOOP",
+ "flags": ["HIDDEN", "CREATE_FROM_PAGE"],
+ "members": [
+ { "name": "securityOrigin", "type": "SecurityOrigin" },
+ { "name": "url", "type": "URL" },
+ { "name": "referrer", "type": "String" }
+ ]
+ },
+ {
+ "name": "FetchPluginData",
+ "description": "Plugin data was requested through DOMPluginArray or DOMMimeTypeArray.",
+ "queue": "SCRIPT_MEMOIZED",
+ "members": [
+ { "name": "pluginData", "type": "PluginData" }
+ ]
+ },
+ {
+ "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" }
+ ]
+ }
+ ]
+ }
+}