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