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