850219745685613342f010e27336ede15fe6d811
[WebKit-https.git] / Source / WebCore / replay / EventLoopInputDispatcher.cpp
1 /*
2  * Copyright (C) 2011-2013 University of Washington. All rights reserved.
3  * Copyright (C) 2014 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "EventLoopInputDispatcher.h"
30
31 #if ENABLE(WEB_REPLAY)
32
33 #include "Page.h"
34 #include "ReplayInputTypes.h"
35 #include "ReplayingInputCursor.h"
36 #include <wtf/TemporaryChange.h>
37
38 #if !LOG_DISABLED
39 #include "Logging.h"
40 #include "SerializationMethods.h"
41 #include <replay/EncodedValue.h>
42 #include <wtf/text/CString.h>
43 #endif
44
45 namespace WebCore {
46
47 EventLoopInputDispatcher::EventLoopInputDispatcher(Page& page, ReplayingInputCursor& cursor, EventLoopInputDispatcherClient* client)
48     : m_page(page)
49     , m_client(client)
50     , m_cursor(cursor)
51     , m_timer(this, &EventLoopInputDispatcher::timerFired)
52     , m_runningInput(nullptr)
53     , m_dispatching(false)
54     , m_running(false)
55     , m_speed(DispatchSpeed::FastForward)
56     , m_previousDispatchStartTime(0.0)
57     , m_previousInputTimestamp(0.0)
58 {
59 }
60
61 void EventLoopInputDispatcher::run()
62 {
63     ASSERT(!m_running);
64     m_running = true;
65
66     LOG(WebReplay, "%-20s Starting dispatch of event loop inputs for page: %p\n", "ReplayEvents", &m_page);
67     dispatchInputSoon();
68 }
69
70 void EventLoopInputDispatcher::pause()
71 {
72     ASSERT(!m_dispatching);
73     ASSERT(m_running);
74     m_running = false;
75
76     LOG(WebReplay, "%-20s Pausing dispatch of event loop inputs for page: %p\n", "ReplayEvents", &m_page);
77     if (m_timer.isActive())
78         m_timer.stop();
79 }
80
81 void EventLoopInputDispatcher::timerFired(Timer<EventLoopInputDispatcher>*)
82 {
83     dispatchInput();
84 }
85
86 void EventLoopInputDispatcher::dispatchInputSoon()
87 {
88     ASSERT(m_running);
89
90     // We may already have an input if replay was paused just before dispatching.
91     if (!m_runningInput)
92         m_runningInput = static_cast<EventLoopInputBase*>(m_cursor.uncheckedLoadInput(InputQueue::EventLoopInput));
93
94     if (m_timer.isActive())
95         m_timer.stop();
96
97     double waitInterval = 0;
98
99     if (m_speed == DispatchSpeed::RealTime) {
100         // The goal is to reproduce the dispatch delay between inputs as it was
101         // was observed during the recording. So, we need to compute how much time
102         // to wait such that the elapsed time plus the wait time will equal the
103         // observed delay between the previous and current input.
104
105         if (!m_previousInputTimestamp)
106             m_previousInputTimestamp = m_runningInput->timestamp();
107
108         double targetInterval = m_runningInput->timestamp() - m_previousInputTimestamp;
109         double elapsed = monotonicallyIncreasingTime() - m_previousDispatchStartTime;
110         waitInterval = targetInterval - elapsed;
111     }
112
113     // A negative wait time means that dispatch took longer on replay than on
114     // capture. In this case, proceed without waiting at all.
115     if (waitInterval < 0)
116         waitInterval = 0;
117
118     if (waitInterval > 1000.0) {
119         LOG_ERROR("%-20s Tried to wait for over 1000 seconds before dispatching next event loop input; this is probably a bug.", "ReplayEvents");
120         waitInterval = 0;
121     }
122
123     LOG(WebReplay, "%-20s (WAIT: %.3f ms)", "ReplayEvents", waitInterval * 1000.0);
124     m_timer.startOneShot(waitInterval);
125 }
126
127 void EventLoopInputDispatcher::dispatchInput()
128 {
129     ASSERT(m_runningInput);
130     ASSERT(!m_dispatching);
131
132     if (m_speed == DispatchSpeed::RealTime) {
133         m_previousDispatchStartTime = monotonicallyIncreasingTime();
134         m_previousInputTimestamp = m_runningInput->timestamp();
135     }
136
137 #if !LOG_DISABLED
138     EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(*m_runningInput);
139     String jsonString = encodedInput.asObject()->toJSONString();
140
141     LOG(WebReplay, "%-20s ----------------------------------------------", "ReplayEvents");
142     LOG(WebReplay, "%-20s >DISPATCH: %s %s\n", "ReplayEvents", m_runningInput->type().string().utf8().data(), jsonString.utf8().data());
143 #endif
144
145     m_client->willDispatchInput(*m_runningInput);
146     // Client could stop replay in the previous callback, so check again.
147     if (!m_running)
148         return;
149
150     {
151         TemporaryChange<bool> change(m_dispatching, true);
152         m_runningInput->dispatch(m_page.replayController());
153     }
154
155     EventLoopInputBase* dispatchedInput = m_runningInput;
156     m_runningInput = nullptr;
157
158     // Notify clients that the event was dispatched.
159     m_client->didDispatchInput(*dispatchedInput);
160     if (dispatchedInput->type() == inputTypes().EndSegmentSentinel) {
161         m_running = false;
162         m_dispatching = false;
163         m_client->didDispatchFinalInput();
164         return;
165     }
166
167     // Clients could stop replay during event dispatch, or from any callback above.
168     if (!m_running)
169         return;
170
171     dispatchInputSoon();
172 }
173
174 } // namespace WebCore
175
176 #endif // ENABLE(WEB_REPLAY)