[cmake] Fix the web replay build
[WebKit-https.git] / Source / WebCore / inspector / InspectorReplayAgent.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 "InspectorReplayAgent.h"
30
31 #if ENABLE(WEB_REPLAY)
32
33 #include "DocumentLoader.h"
34 #include "Event.h"
35 #include "EventLoopInput.h"
36 #include "Frame.h"
37 #include "FunctorInputCursor.h"
38 #include "InspectorController.h"
39 #include "InspectorPageAgent.h"
40 #include <inspector/InspectorProtocolObjects.h>
41 #include "InstrumentingAgents.h"
42 #include "Logging.h"
43 #include "Page.h"
44 #include "ReplayController.h"
45 #include "ReplaySession.h"
46 #include "ReplaySessionSegment.h"
47 #include "SerializationMethods.h"
48 #include "WebReplayInputs.h" // For EncodingTraits<InputQueue>.
49 #include <inspector/InspectorValues.h>
50 #include <wtf/text/CString.h>
51 #include <wtf/text/WTFString.h>
52
53 using namespace Inspector;
54
55 namespace WebCore {
56
57 static Ref<Inspector::Protocol::Replay::ReplayPosition> buildInspectorObjectForPosition(const ReplayPosition& position)
58 {
59     return Inspector::Protocol::Replay::ReplayPosition::create()
60         .setSegmentOffset(position.segmentOffset)
61         .setInputOffset(position.inputOffset)
62         .release();
63 }
64
65 static Ref<Inspector::Protocol::Replay::ReplayInput> buildInspectorObjectForInput(const NondeterministicInputBase& input, size_t offset)
66 {
67     EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(input);
68     return Inspector::Protocol::Replay::ReplayInput::create()
69         .setType(input.type())
70         .setOffset(offset)
71         .setData(encodedInput.asObject())
72         .release();
73 }
74
75 static Ref<Inspector::Protocol::Replay::ReplaySession> buildInspectorObjectForSession(RefPtr<ReplaySession>&& session)
76 {
77     auto segments = Inspector::Protocol::Array<SegmentIdentifier>::create();
78
79     for (auto it = session->begin(); it != session->end(); ++it)
80         segments->addItem((*it)->identifier());
81
82     return Inspector::Protocol::Replay::ReplaySession::create()
83         .setId(session->identifier())
84         .setTimestamp(session->timestamp())
85         .setSegments(WTF::move(segments))
86         .release();
87 }
88
89 static Inspector::Protocol::Replay::SessionState buildInspectorObjectForSessionState(WebCore::SessionState sessionState)
90 {
91     switch (sessionState) {
92     case WebCore::SessionState::Capturing: return Inspector::Protocol::Replay::SessionState::Capturing;
93     case WebCore::SessionState::Inactive: return Inspector::Protocol::Replay::SessionState::Inactive;
94     case WebCore::SessionState::Replaying: return Inspector::Protocol::Replay::SessionState::Replaying;
95     }
96
97     RELEASE_ASSERT_NOT_REACHED();
98     return Inspector::Protocol::Replay::SessionState::Inactive;
99 }
100
101 static Inspector::Protocol::Replay::SegmentState buildInspectorObjectForSegmentState(WebCore::SegmentState segmentState)
102 {
103     switch (segmentState) {
104     case WebCore::SegmentState::Appending: return Inspector::Protocol::Replay::SegmentState::Appending;
105     case WebCore::SegmentState::Unloaded: return Inspector::Protocol::Replay::SegmentState::Unloaded;
106     case WebCore::SegmentState::Loaded: return Inspector::Protocol::Replay::SegmentState::Loaded;
107     case WebCore::SegmentState::Dispatching: return Inspector::Protocol::Replay::SegmentState::Dispatching;
108     }
109
110     RELEASE_ASSERT_NOT_REACHED();
111     return Inspector::Protocol::Replay::SegmentState::Unloaded;
112 }
113
114 class SerializeInputToJSONFunctor {
115 public:
116     typedef RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Replay::ReplayInput>> ReturnType;
117
118     SerializeInputToJSONFunctor()
119         : m_inputs(Inspector::Protocol::Array<Inspector::Protocol::Replay::ReplayInput>::create()) { }
120     ~SerializeInputToJSONFunctor() { }
121
122     void operator()(size_t index, const NondeterministicInputBase* input)
123     {
124         LOG(WebReplay, "%-25s Writing %5zu: %s\n", "[SerializeInput]", index, input->type().ascii().data());
125
126         if (RefPtr<Inspector::Protocol::Replay::ReplayInput> serializedInput = buildInspectorObjectForInput(*input, index))
127             m_inputs->addItem(WTF::move(serializedInput));
128     }
129
130     ReturnType returnValue() { return WTF::move(m_inputs); }
131 private:
132     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Replay::ReplayInput>> m_inputs;
133 };
134
135 static Ref<Inspector::Protocol::Replay::SessionSegment> buildInspectorObjectForSegment(RefPtr<ReplaySessionSegment>&& segment)
136 {
137     auto queuesObject = Inspector::Protocol::Array<Inspector::Protocol::Replay::ReplayInputQueue>::create();
138
139     for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++) {
140         SerializeInputToJSONFunctor collector;
141         InputQueue queue = static_cast<InputQueue>(i);
142         RefPtr<FunctorInputCursor> functorCursor = FunctorInputCursor::create(segment.copyRef());
143         RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Replay::ReplayInput>> queueInputs = functorCursor->forEachInputInQueue(queue, collector);
144
145         auto queueObject = Inspector::Protocol::Replay::ReplayInputQueue::create()
146             .setType(EncodingTraits<InputQueue>::encodeValue(queue).convertTo<String>())
147             .setInputs(queueInputs)
148             .release();
149         queuesObject->addItem(WTF::move(queueObject));
150     }
151
152     return Inspector::Protocol::Replay::SessionSegment::create()
153         .setId(segment->identifier())
154         .setTimestamp(segment->timestamp())
155         .setQueues(WTF::move(queuesObject))
156         .release();
157 }
158
159 InspectorReplayAgent::InspectorReplayAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent)
160     : InspectorAgentBase(ASCIILiteral("Replay"), instrumentingAgents)
161     , m_page(*pageAgent->page())
162 {
163 }
164
165 InspectorReplayAgent::~InspectorReplayAgent()
166 {
167     ASSERT(!m_sessionsMap.size());
168     ASSERT(!m_segmentsMap.size());
169 }
170
171 WebCore::SessionState InspectorReplayAgent::sessionState() const
172 {
173     return m_page.replayController().sessionState();
174 }
175
176 void InspectorReplayAgent::didCreateFrontendAndBackend(Inspector::FrontendChannel* frontendChannel, Inspector::BackendDispatcher* backendDispatcher)
177 {
178     m_frontendDispatcher = std::make_unique<Inspector::ReplayFrontendDispatcher>(frontendChannel);
179     m_backendDispatcher = Inspector::ReplayBackendDispatcher::create(backendDispatcher, this);
180
181     m_instrumentingAgents->setInspectorReplayAgent(this);
182     ASSERT(sessionState() == WebCore::SessionState::Inactive);
183
184     // Keep track of the (default) session currently loaded by ReplayController,
185     // and any segments within the session.
186     RefPtr<ReplaySession> session = m_page.replayController().loadedSession();
187     m_sessionsMap.add(session->identifier(), session);
188
189     for (auto it = session->begin(); it != session->end(); ++it)
190         m_segmentsMap.add((*it)->identifier(), *it);
191 }
192
193 void InspectorReplayAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
194 {
195     m_frontendDispatcher = nullptr;
196     m_backendDispatcher.clear();
197
198     m_instrumentingAgents->setInspectorReplayAgent(nullptr);
199
200     // Drop references to all sessions and segments.
201     m_sessionsMap.clear();
202     m_segmentsMap.clear();
203 }
204
205 void InspectorReplayAgent::frameNavigated(DocumentLoader* loader)
206 {
207     if (sessionState() != WebCore::SessionState::Inactive)
208         m_page.replayController().frameNavigated(loader);
209 }
210
211 void InspectorReplayAgent::frameDetached(Frame& frame)
212 {
213     if (sessionState() != WebCore::SessionState::Inactive)
214         m_page.replayController().frameDetached(frame);
215 }
216
217 void InspectorReplayAgent::willDispatchEvent(const Event& event, Frame* frame)
218 {
219     if (sessionState() != WebCore::SessionState::Inactive)
220         m_page.replayController().willDispatchEvent(event, frame);
221 }
222
223 void InspectorReplayAgent::sessionCreated(RefPtr<ReplaySession>&& session)
224 {
225     auto result = m_sessionsMap.add(session->identifier(), session);
226     // Can't have two sessions with same identifier.
227     ASSERT_UNUSED(result, result.isNewEntry);
228
229     m_frontendDispatcher->sessionCreated(session->identifier());
230 }
231
232 void InspectorReplayAgent::sessionModified(RefPtr<ReplaySession>&& session)
233 {
234     m_frontendDispatcher->sessionModified(session->identifier());
235 }
236
237 void InspectorReplayAgent::sessionLoaded(RefPtr<ReplaySession>&& session)
238 {
239     // In case we didn't know about the loaded session, add here.
240     m_sessionsMap.add(session->identifier(), session);
241
242     m_frontendDispatcher->sessionLoaded(session->identifier());
243 }
244
245 void InspectorReplayAgent::segmentCreated(RefPtr<ReplaySessionSegment>&& segment)
246 {
247     auto result = m_segmentsMap.add(segment->identifier(), segment);
248     // Can't have two segments with the same identifier.
249     ASSERT_UNUSED(result, result.isNewEntry);
250
251     m_frontendDispatcher->segmentCreated(segment->identifier());
252 }
253
254 void InspectorReplayAgent::segmentCompleted(RefPtr<ReplaySessionSegment>&& segment)
255 {
256     m_frontendDispatcher->segmentCompleted(segment->identifier());
257 }
258
259 void InspectorReplayAgent::segmentLoaded(RefPtr<ReplaySessionSegment>&& segment)
260 {
261     // In case we didn't know about the loaded segment, add here.
262     m_segmentsMap.add(segment->identifier(), segment.copyRef());
263
264     m_frontendDispatcher->segmentLoaded(segment->identifier());
265 }
266
267 void InspectorReplayAgent::segmentUnloaded()
268 {
269     m_frontendDispatcher->segmentUnloaded();
270 }
271
272 void InspectorReplayAgent::captureStarted()
273 {
274     LOG(WebReplay, "-----CAPTURE START-----");
275
276     m_frontendDispatcher->captureStarted();
277 }
278
279 void InspectorReplayAgent::captureStopped()
280 {
281     LOG(WebReplay, "-----CAPTURE STOP-----");
282
283     m_frontendDispatcher->captureStopped();
284 }
285
286 void InspectorReplayAgent::playbackStarted()
287 {
288     LOG(WebReplay, "-----REPLAY START-----");
289
290     m_frontendDispatcher->playbackStarted();
291 }
292
293 void InspectorReplayAgent::playbackPaused(const ReplayPosition& position)
294 {
295     LOG(WebReplay, "-----REPLAY PAUSED-----");
296
297     m_frontendDispatcher->playbackPaused(buildInspectorObjectForPosition(position));
298 }
299
300 void InspectorReplayAgent::playbackHitPosition(const ReplayPosition& position)
301 {
302     m_frontendDispatcher->playbackHitPosition(buildInspectorObjectForPosition(position), monotonicallyIncreasingTime());
303 }
304
305 void InspectorReplayAgent::playbackFinished()
306 {
307     LOG(WebReplay, "-----REPLAY FINISHED-----");
308
309     m_frontendDispatcher->playbackFinished();
310 }
311
312 void InspectorReplayAgent::startCapturing(ErrorString& errorString)
313 {
314     if (sessionState() != WebCore::SessionState::Inactive) {
315         errorString = ASCIILiteral("Can't start capturing if the session is already capturing or replaying.");
316         return;
317     }
318
319     m_page.replayController().startCapturing();
320 }
321
322 void InspectorReplayAgent::stopCapturing(ErrorString& errorString)
323 {
324     if (sessionState() != WebCore::SessionState::Capturing) {
325         errorString = ASCIILiteral("Can't stop capturing if capture is not in progress.");
326         return;
327     }
328
329     m_page.replayController().stopCapturing();
330 }
331
332 void InspectorReplayAgent::replayToPosition(ErrorString& errorString, const RefPtr<InspectorObject>&& positionObject, bool fastReplay)
333 {
334     ReplayPosition position;
335     if (!positionObject->getInteger(ASCIILiteral("segmentOffset"), position.segmentOffset)) {
336         errorString = ASCIILiteral("Couldn't decode ReplayPosition segment offset provided to ReplayAgent.replayToPosition.");
337         return;
338     }
339
340     if (!positionObject->getInteger(ASCIILiteral("inputOffset"), position.inputOffset)) {
341         errorString = ASCIILiteral("Couldn't decode ReplayPosition input offset provided to ReplayAgent.replayToPosition.");
342         return;
343     }
344
345     if (sessionState() == WebCore::SessionState::Capturing) {
346         errorString = ASCIILiteral("Can't start replay while capture is in progress.");
347         return;
348     }
349
350     m_page.replayController().replayToPosition(position, (fastReplay) ? DispatchSpeed::FastForward : DispatchSpeed::RealTime);
351 }
352
353 void InspectorReplayAgent::replayToCompletion(ErrorString& errorString, bool fastReplay)
354 {
355     if (sessionState() == WebCore::SessionState::Capturing) {
356         errorString = ASCIILiteral("Can't start replay while capture is in progress.");
357         return;
358     }
359
360     m_page.replayController().replayToCompletion((fastReplay) ? DispatchSpeed::FastForward : DispatchSpeed::RealTime);
361 }
362
363 void InspectorReplayAgent::pausePlayback(ErrorString& errorString)
364 {
365     if (sessionState() != WebCore::SessionState::Replaying) {
366         errorString = ASCIILiteral("Can't pause playback if playback is not in progress.");
367         return;
368     }
369
370     m_page.replayController().pausePlayback();
371 }
372
373 void InspectorReplayAgent::cancelPlayback(ErrorString& errorString)
374 {
375     if (sessionState() == WebCore::SessionState::Capturing) {
376         errorString = ASCIILiteral("Can't cancel playback if capture is in progress.");
377         return;
378     }
379
380     m_page.replayController().cancelPlayback();
381 }
382
383 void InspectorReplayAgent::switchSession(ErrorString& errorString, Inspector::Protocol::Replay::SessionIdentifier identifier)
384 {
385     ASSERT_ARG(identifier, identifier > 0);
386
387     if (sessionState() != WebCore::SessionState::Inactive) {
388         errorString = ASCIILiteral("Can't switch sessions unless the session is neither capturing or replaying.");
389         return;
390     }
391
392     RefPtr<ReplaySession> session = findSession(errorString, identifier);
393     if (!session)
394         return;
395
396     m_page.replayController().switchSession(WTF::move(session));
397 }
398
399 void InspectorReplayAgent::insertSessionSegment(ErrorString& errorString, Inspector::Protocol::Replay::SessionIdentifier sessionIdentifier, SegmentIdentifier segmentIdentifier, int segmentIndex)
400 {
401     ASSERT_ARG(sessionIdentifier, sessionIdentifier > 0);
402     ASSERT_ARG(segmentIdentifier, segmentIdentifier > 0);
403     ASSERT_ARG(segmentIndex, segmentIndex >= 0);
404
405     RefPtr<ReplaySession> session = findSession(errorString, sessionIdentifier);
406     RefPtr<ReplaySessionSegment> segment = findSegment(errorString, segmentIdentifier);
407
408     if (!session || !segment)
409         return;
410
411     if (static_cast<size_t>(segmentIndex) > session->size()) {
412         errorString = ASCIILiteral("Invalid segment index.");
413         return;
414     }
415
416     if (session == m_page.replayController().loadedSession() && sessionState() != WebCore::SessionState::Inactive) {
417         errorString = ASCIILiteral("Can't modify a loaded session unless the session is inactive.");
418         return;
419     }
420
421     session->insertSegment(segmentIndex, WTF::move(segment));
422     sessionModified(WTF::move(session));
423 }
424
425 void InspectorReplayAgent::removeSessionSegment(ErrorString& errorString, Inspector::Protocol::Replay::SessionIdentifier identifier, int segmentIndex)
426 {
427     ASSERT_ARG(identifier, identifier > 0);
428     ASSERT_ARG(segmentIndex, segmentIndex >= 0);
429
430     RefPtr<ReplaySession> session = findSession(errorString, identifier);
431
432     if (!session)
433         return;
434
435     if (static_cast<size_t>(segmentIndex) >= session->size()) {
436         errorString = ASCIILiteral("Invalid segment index.");
437         return;
438     }
439
440     if (session == m_page.replayController().loadedSession() && sessionState() != WebCore::SessionState::Inactive) {
441         errorString = ASCIILiteral("Can't modify a loaded session unless the session is inactive.");
442         return;
443     }
444
445     session->removeSegment(segmentIndex);
446     sessionModified(WTF::move(session));
447 }
448
449 RefPtr<ReplaySession> InspectorReplayAgent::findSession(ErrorString& errorString, SessionIdentifier identifier)
450 {
451     ASSERT_ARG(identifier, identifier > 0);
452
453     auto it = m_sessionsMap.find(identifier);
454     if (it == m_sessionsMap.end()) {
455         errorString = ASCIILiteral("Couldn't find session with specified identifier");
456         return nullptr;
457     }
458
459     return it->value;
460 }
461
462 RefPtr<ReplaySessionSegment> InspectorReplayAgent::findSegment(ErrorString& errorString, SegmentIdentifier identifier)
463 {
464     ASSERT_ARG(identifier, identifier > 0);
465
466     auto it = m_segmentsMap.find(identifier);
467     if (it == m_segmentsMap.end()) {
468         errorString = ASCIILiteral("Couldn't find segment with specified identifier");
469         return nullptr;
470     }
471
472     return it->value;
473 }
474
475 void InspectorReplayAgent::currentReplayState(ErrorString&, Inspector::Protocol::Replay::SessionIdentifier* sessionIdentifier, Inspector::Protocol::OptOutput<Inspector::Protocol::Replay::SegmentIdentifier>* segmentIdentifier, Inspector::Protocol::Replay::SessionState* sessionState, Inspector::Protocol::Replay::SegmentState* segmentState, RefPtr<Inspector::Protocol::Replay::ReplayPosition>& replayPosition)
476 {
477     *sessionState = buildInspectorObjectForSessionState(m_page.replayController().sessionState());
478     *segmentState = buildInspectorObjectForSegmentState(m_page.replayController().segmentState());
479
480     *sessionIdentifier = m_page.replayController().loadedSession()->identifier();
481     if (m_page.replayController().loadedSegment())
482         *segmentIdentifier = m_page.replayController().loadedSegment()->identifier();
483
484     replayPosition = buildInspectorObjectForPosition(m_page.replayController().currentPosition());
485 }
486
487 void InspectorReplayAgent::getAvailableSessions(ErrorString&, RefPtr<Inspector::Protocol::Array<SessionIdentifier>>& sessionsList)
488 {
489     sessionsList = Inspector::Protocol::Array<Inspector::Protocol::Replay::SessionIdentifier>::create();
490     for (auto& pair : m_sessionsMap)
491         sessionsList->addItem(pair.key);
492 }
493
494 void InspectorReplayAgent::getSessionData(ErrorString& errorString, Inspector::Protocol::Replay::SessionIdentifier identifier, RefPtr<Inspector::Protocol::Replay::ReplaySession>& serializedObject)
495 {
496     RefPtr<ReplaySession> session = findSession(errorString, identifier);
497     if (!session) {
498         errorString = ASCIILiteral("Couldn't find the specified session.");
499         return;
500     }
501
502     serializedObject = buildInspectorObjectForSession(WTF::move(session));
503 }
504
505 void InspectorReplayAgent::getSegmentData(ErrorString& errorString, Inspector::Protocol::Replay::SegmentIdentifier identifier, RefPtr<Inspector::Protocol::Replay::SessionSegment>& serializedObject)
506 {
507     RefPtr<ReplaySessionSegment> segment = findSegment(errorString, identifier);
508     if (!segment) {
509         errorString = ASCIILiteral("Couldn't find the specified segment.");
510         return;
511     }
512
513     serializedObject = buildInspectorObjectForSegment(WTF::move(segment));
514 }
515
516 } // namespace WebCore
517
518 #endif // ENABLE(WEB_REPLAY)