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