blob: cd8b93c30e62b47aa3a39807a88f4d81a961cc99 [file] [log] [blame]
/*
* 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/TemporaryChange.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;
{
TemporaryChange<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)