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