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