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