WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
patch part 1 - v1
bug-128782-20140220185757.patch (text/plain), 192.61 KB, created by
Blaze Burg
on 2014-02-20 18:57:58 PST
(
hide
)
Description:
patch part 1 - v1
Filename:
MIME Type:
Creator:
Blaze Burg
Created:
2014-02-20 18:57:58 PST
Size:
192.61 KB
patch
obsolete
>Subversion Revision: 164462 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index fe4700fb4fe6b9c0f180bb23199293d7b57fc3ff..fe15de2ca70b95385ebfd7a686991cc4ebb30bd9 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,22 @@ >+2014-02-20 Brian Burg <bburg@apple.com> >+ >+ Web Replay: upstream input storage, capture/replay machinery, and inspector domain >+ https://bugs.webkit.org/show_bug.cgi?id=128782 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Alter the replay inputs code generator so that it knows when it is necessary to >+ to include headers for HEAVY_SCALAR types such as WTF::String and WebCore::URL. >+ >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * replay/scripts/CodeGeneratorReplayInputs.py: >+ (Framework.fromString): >+ (Frameworks): Add WTF as an allowed framework for code generation. >+ (Generator.generate_includes): Include headers for HEAVY_SCALAR types in the header file. >+ (Generator.generate_includes.declaration): >+ (Generator.generate_includes.or): >+ (Generator.generate_type_forward_declarations): Skip HEAVY_SCALAR types. >+ > 2014-02-20 Geoffrey Garen <ggaren@apple.com> > > Math.imul gives wrong results >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index e9e179d4a4cc7f392e4e5b3301e75b73876893ef..c2baa9531918a942c7c652662335f004fbef93ba 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,273 @@ >+2014-02-20 Brian Burg <bburg@apple.com> >+ >+ Web Replay: upstream input storage, capture/replay machinery, and inspector domain >+ https://bugs.webkit.org/show_bug.cgi?id=128782 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ No new tests yet, as they rely on part 2 of this bug. >+ >+ Replayable executions are organized into ReplaySessions, which can >+ contain several ReplaySessionSegments that divide overall execution >+ at main frame navigation boundaries. NondeterministicInput subclasses >+ are stored in SegmentedInputStorage according to the input's InputQueue. >+ >+ Capture and playback are controlled at the page granularity by the Page's >+ ReplayController. The controller knows how to create new segments, replay to >+ arbitrary positions in the ReplaySession, and track the active InputCursor. >+ >+ The capturing and replaying input cursor subclasses encapsulate state for >+ storing new inputs and loading/dispatching saved inputs, respectively. >+ >+ The ReplayAgent and associated inspector protocol domain is the friendly >+ public API for programmatically capturing and replaying sessions. >+ >+ * DerivedSources.make: Add replay inputs code generation target. Add the >+ replay domain specification to the list of inspector domains. >+ >+ * ForwardingHeaders/replay/EncodedValue.h: Added. >+ * WebCore.xcodeproj/project.pbxproj: Add many files, and export >+ `WebReplayScripts` environment variable to DerivedSources.make. >+ >+ * inspector/InspectorController.cpp: Add the replay agent. >+ (WebCore::InspectorController::InspectorController): >+ >+ * inspector/InspectorInstrumentation.cpp: >+ Add events for segment lifecycle events, and loading/unloading of sessions >+ and segments, and capture/replay progress events. The replay controller >+ also needs to know about detached and committed frames. >+ >+ (WebCore::InspectorInstrumentation::frameDetachedFromParentImpl): >+ (WebCore::InspectorInstrumentation::didCommitLoadImpl): >+ (WebCore::InspectorInstrumentation::sessionCreatedImpl): >+ (WebCore::InspectorInstrumentation::sessionLoadedImpl): >+ (WebCore::InspectorInstrumentation::sessionModifiedImpl): >+ (WebCore::InspectorInstrumentation::segmentCreatedImpl): >+ (WebCore::InspectorInstrumentation::segmentCompletedImpl): >+ (WebCore::InspectorInstrumentation::segmentLoadedImpl): >+ (WebCore::InspectorInstrumentation::segmentUnloadedImpl): >+ (WebCore::InspectorInstrumentation::captureStartedImpl): >+ (WebCore::InspectorInstrumentation::captureStoppedImpl): >+ (WebCore::InspectorInstrumentation::playbackStartedImpl): >+ (WebCore::InspectorInstrumentation::playbackPausedImpl): >+ (WebCore::InspectorInstrumentation::playbackHitPositionImpl): >+ (WebCore::InspectorInstrumentation::replayAgentEnabled): >+ * inspector/InspectorInstrumentation.h: >+ (WebCore::InspectorInstrumentation::replayAgentEnabled): >+ (WebCore::InspectorInstrumentation::sessionCreated): >+ (WebCore::InspectorInstrumentation::sessionLoaded): >+ (WebCore::InspectorInstrumentation::sessionModified): >+ (WebCore::InspectorInstrumentation::segmentCreated): >+ (WebCore::InspectorInstrumentation::segmentCompleted): >+ (WebCore::InspectorInstrumentation::segmentLoaded): >+ (WebCore::InspectorInstrumentation::segmentUnloaded): >+ (WebCore::InspectorInstrumentation::captureStarted): >+ (WebCore::InspectorInstrumentation::captureStopped): >+ (WebCore::InspectorInstrumentation::playbackStarted): >+ (WebCore::InspectorInstrumentation::playbackPaused): >+ (WebCore::InspectorInstrumentation::playbackHitPosition): >+ >+ * inspector/InspectorReplayAgent.cpp: Added. >+ (WebCore::buildInspectorObjectForPosition): >+ (WebCore::buildInspectorObjectForInput): >+ (WebCore::buildInspectorObjectForSession): >+ (WebCore::SerializeInputToJSONFunctor::SerializeInputToJSONFunctor): >+ (WebCore::SerializeInputToJSONFunctor::~SerializeInputToJSONFunctor): >+ (WebCore::SerializeInputToJSONFunctor::operator()): >+ (WebCore::SerializeInputToJSONFunctor::returnValue): >+ (WebCore::buildInspectorObjectForSegment): >+ (WebCore::InspectorReplayAgent::InspectorReplayAgent): >+ (WebCore::InspectorReplayAgent::~InspectorReplayAgent): >+ (WebCore::InspectorReplayAgent::sessionState): >+ (WebCore::InspectorReplayAgent::didCreateFrontendAndBackend): >+ (WebCore::InspectorReplayAgent::willDestroyFrontendAndBackend): >+ (WebCore::InspectorReplayAgent::frameNavigated): >+ (WebCore::InspectorReplayAgent::frameDetached): >+ (WebCore::InspectorReplayAgent::sessionCreated): >+ (WebCore::InspectorReplayAgent::sessionModified): >+ (WebCore::InspectorReplayAgent::sessionLoaded): >+ (WebCore::InspectorReplayAgent::segmentCreated): >+ (WebCore::InspectorReplayAgent::segmentCompleted): >+ (WebCore::InspectorReplayAgent::segmentLoaded): >+ (WebCore::InspectorReplayAgent::segmentUnloaded): >+ (WebCore::InspectorReplayAgent::captureStarted): >+ (WebCore::InspectorReplayAgent::captureStopped): >+ (WebCore::InspectorReplayAgent::playbackStarted): >+ (WebCore::InspectorReplayAgent::playbackPaused): >+ (WebCore::InspectorReplayAgent::playbackHitPosition): >+ (WebCore::InspectorReplayAgent::startCapturing): >+ (WebCore::InspectorReplayAgent::stopCapturing): >+ (WebCore::InspectorReplayAgent::replayToPosition): >+ (WebCore::InspectorReplayAgent::replayToCompletion): >+ (WebCore::InspectorReplayAgent::pausePlayback): >+ (WebCore::InspectorReplayAgent::cancelPlayback): >+ (WebCore::InspectorReplayAgent::switchSession): >+ (WebCore::InspectorReplayAgent::insertSessionSegment): >+ (WebCore::InspectorReplayAgent::removeSessionSegment): >+ Provide a public API for modifying sessions. This is the backend support >+ for user editing of replay sessions to add/remove specific segments. >+ >+ (WebCore::InspectorReplayAgent::findSession): >+ (WebCore::InspectorReplayAgent::findSegment): >+ (WebCore::InspectorReplayAgent::getAvailableSessions): >+ (WebCore::InspectorReplayAgent::getSerializedSession): >+ (WebCore::InspectorReplayAgent::getSerializedSegment): >+ Most of the replay protocol domain speaks in terms of sesssion and >+ segment identifiers. These functions return the actual data associated >+ with these identifiers. >+ >+ * inspector/InspectorReplayAgent.h: Added. >+ * inspector/InstrumentingAgents.cpp: >+ (WebCore::InstrumentingAgents::InstrumentingAgents): >+ (WebCore::InstrumentingAgents::reset): >+ * inspector/InstrumentingAgents.h: >+ (WebCore::InstrumentingAgents::inspectorReplayAgent): Added. >+ (WebCore::InstrumentingAgents::setInspectorReplayAgent): Added. >+ * inspector/protocol/Replay.json: Added. >+ >+ * page/Page.cpp: >+ (WebCore::Page::Page): >+ * page/Page.h: >+ (WebCore::Page::replayController): Added. >+ * platform/Logging.h: Add WebReplay logging channel. >+ * replay/AllReplayInputs.h: Added. Simplifies importing all input definitions. >+ >+ * replay/CapturingInputCursor.cpp: Added. >+ (WebCore::CapturingInputCursor::CapturingInputCursor): >+ (WebCore::CapturingInputCursor::~CapturingInputCursor): >+ (WebCore::CapturingInputCursor::create): >+ (WebCore::CapturingInputCursor::storeInput): >+ (WebCore::CapturingInputCursor::loadInput): >+ (WebCore::CapturingInputCursor::uncheckedLoadInput): >+ * replay/CapturingInputCursor.h: Added. >+ >+ * replay/EventLoopInput.h: >+ (WebCore::EventLoopInputBase::EventLoopInputBase): >+ (WebCore::EventLoopInputBase::timestamp): >+ (WebCore::EventLoopInputBase::setTimestamp): Support deserialization. >+ >+ * replay/EventLoopInputDispatcher.cpp: Added. This class encapsulates the timers >+ and measurements used to dispatch event loop inputs during replay. >+ >+ (WebCore::EventLoopInputDispatcher::EventLoopInputDispatcher): >+ (WebCore::EventLoopInputDispatcher::run): >+ (WebCore::EventLoopInputDispatcher::pause): >+ (WebCore::EventLoopInputDispatcher::timerFired): >+ (WebCore::EventLoopInputDispatcher::dispatchInputSoon): >+ (WebCore::EventLoopInputDispatcher::dispatchInput): >+ * replay/EventLoopInputDispatcher.h: Added. >+ (WebCore::EventLoopInputDispatcherClient::EventLoopInputDispatcherClient): >+ (WebCore::EventLoopInputDispatcherClient::~EventLoopInputDispatcherClient): >+ >+ * replay/FunctorInputCursor.h: Added. >+ (WebCore::FunctorInputCursor::~FunctorInputCursor): >+ (WebCore::FunctorInputCursor::forEachInputInQueue): >+ (WebCore::FunctorInputCursor::FunctorInputCursor): >+ (WebCore::FunctorInputCursor::storeInput): >+ (WebCore::FunctorInputCursor::loadInput): >+ (WebCore::FunctorInputCursor::uncheckedLoadInput): >+ >+ * replay/ReplayController.cpp: Added. >+ (WebCore::ReplayController::ReplayController): >+ (WebCore::ReplayController::switchSession): >+ (WebCore::ReplayController::createSegment): >+ (WebCore::ReplayController::completeSegment): >+ (WebCore::ReplayController::loadSegment): >+ (WebCore::ReplayController::unloadSegment): >+ (WebCore::ReplayController::startCapturing): >+ (WebCore::ReplayController::stopCapturing): >+ (WebCore::ReplayController::startPlayback): >+ (WebCore::ReplayController::pausePlayback): >+ (WebCore::ReplayController::cancelPlayback): >+ (WebCore::ReplayController::replayToPosition): >+ (WebCore::ReplayController::frameDetached): >+ (WebCore::ReplayController::frameNavigated): >+ (WebCore::ReplayController::loadedSession): >+ (WebCore::ReplayController::loadedSegment): >+ (WebCore::ReplayController::activeInputCursor): >+ (WebCore::ReplayController::dispatcher): >+ (WebCore::ReplayController::willDispatchInput): >+ (WebCore::ReplayController::didDispatchInput): >+ (WebCore::ReplayController::didDispatchFinalInput): >+ * replay/ReplayController.h: Added. >+ (WebCore::ReplayPosition::ReplayPosition): >+ (WebCore::ReplayPosition::operator<): >+ (WebCore::ReplayPosition::operator==): >+ >+ * replay/ReplayInputCreationMethods.cpp: Added. >+ Static factory implementations for inputs belong here. >+ (WebCore::InitialNavigation::createFromPage): >+ >+ * replay/ReplayInputDispatchMethods.cpp: Added. >+ All dispatch() implementations for generated replay inputs belong here. >+ (WebCore::BeginSegmentSentinel::dispatch): >+ (WebCore::EndSegmentSentinel::dispatch): >+ (WebCore::InitialNavigation::dispatch): >+ >+ * replay/ReplayInputTypes.cpp: >+ (WebCore::ReplayInputTypes::ReplayInputTypes): >+ * replay/ReplayInputTypes.h: Define strings for WebCore inputs. >+ >+ * replay/ReplaySession.cpp: Added. >+ (WebCore::ReplaySession::create): >+ (WebCore::ReplaySession::ReplaySession): >+ (WebCore::ReplaySession::~ReplaySession): >+ (WebCore::ReplaySession::appendSegment): >+ (WebCore::ReplaySession::insertSegment): >+ (WebCore::ReplaySession::removeSegment): >+ * replay/ReplaySession.h: Added. >+ (WebCore::ReplaySession::identifier): >+ (WebCore::ReplaySession::timestamp): >+ (WebCore::ReplaySession::size): >+ (WebCore::ReplaySession::at): >+ (WebCore::ReplaySession::begin): >+ (WebCore::ReplaySession::end): >+ >+ * replay/ReplaySessionSegment.cpp: Added. >+ (WebCore::ReplaySessionSegment::create): >+ (WebCore::ReplaySessionSegment::ReplaySessionSegment): >+ (WebCore::ReplaySessionSegment::~ReplaySessionSegment): >+ (WebCore::ReplaySessionSegment::createCapturingCursor): >+ (WebCore::ReplaySessionSegment::createReplayingCursor): >+ (WebCore::ReplaySessionSegment::createFunctorCursor): >+ * replay/ReplaySessionSegment.h: Added. >+ (WebCore::ReplaySessionSegment::identifier): >+ (WebCore::ReplaySessionSegment::timestamp): >+ >+ * replay/ReplayingInputCursor.cpp: Added. >+ (WebCore::ReplayingInputCursor::ReplayingInputCursor): >+ (WebCore::ReplayingInputCursor::~ReplayingInputCursor): >+ (WebCore::ReplayingInputCursor::create): >+ (WebCore::ReplayingInputCursor::storeInput): >+ (WebCore::ReplayingInputCursor::loadInput): >+ (WebCore::ReplayingInputCursor::uncheckedLoadInput): >+ * replay/ReplayingInputCursor.h: Added. >+ >+ * replay/SegmentedInputStorage.cpp: Added. >+ (WebCore::queueTypeToLogPrefix): >+ (WebCore::jsonStringForInput): >+ (WebCore::offsetForInputQueue): >+ (WebCore::SegmentedInputStorage::SegmentedInputStorage): >+ (WebCore::SegmentedInputStorage::~SegmentedInputStorage): >+ (WebCore::SegmentedInputStorage::load): >+ (WebCore::SegmentedInputStorage::store): >+ (WebCore::SegmentedInputStorage::queueSize): >+ * replay/SegmentedInputStorage.h: Added. >+ >+ * replay/SerializationMethods.cpp: Added. >+ Specializations of EncodingTraits for WebCore types belong here. >+ >+ (JSC::EncodingTraits<NondeterministicInputBase>::encodeValue): >+ (JSC::EncodingTraits<NondeterministicInputBase>::decodeValue): >+ (JSC::EncodingTraits<SecurityOrigin>::encodeValue): >+ (JSC::EncodingTraits<SecurityOrigin>::decodeValue): >+ (JSC::EncodingTraits<URL>::encodeValue): >+ (JSC::EncodingTraits<URL>::decodeValue): >+ * replay/SerializationMethods.h: Added. >+ * replay/WebInputs.json: Added. >+ In this inital patch, we define BeginSegmentSentinel, >+ EndSegmentSentinel, and InitialNavigation inputs. >+ > 2014-02-20 Brady Eidson <beidson@apple.com> > > Move the image controls setting to Settings.in >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index 7fe132bc0520f5bbcf95c154d77e328ea7de01c4..ce49fc26b4ab14a586a2cd30ed74edca3e11bc32 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -943,6 +943,8 @@ > 969A079B0ED1D3AE00F1F681 /* Opcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07950ED1D3AE00F1F681 /* Opcode.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 978801401471AD920041B016 /* JSDateMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9788FC221471AD0C0068CE2D /* JSDateMath.cpp */; }; > 978801411471AD920041B016 /* JSDateMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 9788FC231471AD0C0068CE2D /* JSDateMath.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 990A1A0D18ADA7E900183FD1 /* CodeGeneratorReplayInputs.py in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1D18A1B1E70026D88F /* CodeGeneratorReplayInputs.py */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 990A1A0E18ADA7E900183FD1 /* CodeGeneratorReplayInputsTemplates.py in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1E18A1B1E70026D88F /* CodeGeneratorReplayInputsTemplates.py */; settings = {ATTRIBUTES = (Private, ); }; }; > 9928FF3B18AC4AEC00B8CF12 /* JSReplayInputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9928FF3918AC4AEC00B8CF12 /* JSReplayInputs.cpp */; }; > 9928FF3C18AC4AEC00B8CF12 /* JSReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 9928FF3A18AC4AEC00B8CF12 /* JSReplayInputs.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 99E45A2418A1B2590026D88F /* EmptyInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1F18A1B2590026D88F /* EmptyInputCursor.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -4824,6 +4826,8 @@ > 86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */, > 86ADD1450FDDEA980006EEC2 /* ARMv7Assembler.h in Headers */, > A5CEEE15187F3BAD00E55C99 /* InspectorAgent.h in Headers */, >+ 990A1A0D18ADA7E900183FD1 /* CodeGeneratorReplayInputs.py in Headers */, >+ 990A1A0E18ADA7E900183FD1 /* CodeGeneratorReplayInputsTemplates.py in Headers */, > 2A343F7618A1748B0039B085 /* GCSegmentedArray.h in Headers */, > 2A343F7818A1749D0039B085 /* GCSegmentedArrayInlines.h in Headers */, > 65C0285D1717966800351E35 /* ARMv7DOpcode.h in Headers */, >diff --git a/Source/JavaScriptCore/replay/scripts/CodeGeneratorReplayInputs.py b/Source/JavaScriptCore/replay/scripts/CodeGeneratorReplayInputs.py >index e3c81996a1762bd48e3e5aa176e3e95b16fd19f8..cd65108e16c2eab79ee09c7e26d5a332184cf13b 100644 >--- a/Source/JavaScriptCore/replay/scripts/CodeGeneratorReplayInputs.py >+++ b/Source/JavaScriptCore/replay/scripts/CodeGeneratorReplayInputs.py >@@ -103,6 +103,10 @@ FRAMEWORK_CONFIG_MAP = { > "namespace": "" > }, > >+ "WTF": { >+ "prefix": "WTF", >+ "namespace": "WTF", >+ }, > "JavaScriptCore": { > "prefix": "JS", > "namespace": "JSC", >@@ -166,6 +170,9 @@ class Framework: > if frameworkString == "Global": > return Frameworks.Global > >+ if frameworkString == "WTF": >+ return Frameworks.WTF >+ > if frameworkString == "JavaScriptCore": > return Frameworks.JavaScriptCore > >@@ -180,6 +187,7 @@ class Framework: > > class Frameworks: > Global = Framework("Global") >+ WTF = Framework("WTF") > JavaScriptCore = Framework("JavaScriptCore") > WebCore = Framework("WebCore") > Test = Framework("Test") >@@ -616,11 +624,14 @@ class Generator: > if _type.framework is Frameworks.Global: > continue > # For RefCounted types, we reverse when to include the header so that the destructor can be >- # used in the header file. Enums within classes cannot be forward declared, so we include >- # headers with the relevant class declaration. >+ # used in the header file. > include_for_destructor = _type.mode is TypeModes.SHARED >+ # Enums within classes cannot be forward declared, so we include >+ # headers with the relevant class declaration. > include_for_enclosing_class = _type.is_enum() and _type.enclosing_class is not None >- if (not includes_for_types) ^ (include_for_destructor or include_for_enclosing_class): >+ # Include headers for types like URL and String which are copied, not owned or shared. >+ include_for_copyable_member = _type.mode is TypeModes.HEAVY_SCALAR or _type.mode is TypeModes.SCALAR >+ if (not includes_for_types) ^ (include_for_destructor or include_for_enclosing_class or include_for_copyable_member): > continue > > if self.target_framework != _type.framework: >@@ -654,6 +665,8 @@ class Generator: > continue > if _type.enclosing_class is not None: > continue >+ if _type.mode == TypeModes.SCALAR or _type.mode == TypeModes.HEAVY_SCALAR: >+ continue > if _type.is_enum(): > declaration = "enum %s : %s;" % (_type.type_name(), _type.underlying_storage) > else: >diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make >index f7f4a8c91ad3efa7a6ba666f6b7ab28f82d62f8f..733082c3dd38cb9926a8669bd7b0eb62b8e3a729 100644 >--- a/Source/WebCore/DerivedSources.make >+++ b/Source/WebCore/DerivedSources.make >@@ -1101,6 +1101,7 @@ INSPECTOR_DOMAINS = \ > $(WebCore)/inspector/protocol/Network.json \ > $(WebCore)/inspector/protocol/Page.json \ > $(WebCore)/inspector/protocol/Profiler.json \ >+ $(WebCore)/inspector/protocol/Replay.json \ > $(WebCore)/inspector/protocol/Timeline.json \ > $(WebCore)/inspector/protocol/Worker.json \ > # >@@ -1141,6 +1142,22 @@ InjectedScriptCanvasModuleSource.h : InjectedScriptCanvasModuleSource.js > perl $(InspectorScripts)/xxd.pl InjectedScriptCanvasModuleSource_js ./InjectedScriptCanvasModuleSource.min.js InjectedScriptCanvasModuleSource.h > rm -f ./InjectedScriptCanvasModuleSource.min.js > >+# Web Replay inputs generator >+ >+INPUT_GENERATOR_SCRIPTS = \ >+ $(WebReplayScripts)/CodeGeneratorReplayInputs.py \ >+ $(WebReplayScripts)/CodeGeneratorReplayInputsTemplates.py \ >+# >+ >+INPUT_GENERATOR_SPECIFICATIONS = \ >+ $(WebCore)/replay/WebInputs.json \ >+# >+ >+all : WebReplayInputs.h >+ >+WebReplayInputs.h : $(INPUT_GENERATOR_SPECIFICATIONS) $(INPUT_GENERATOR_SCRIPTS) >+ python $(WebReplayScripts)/CodeGeneratorReplayInputs.py --outputDir . --framework WebCore $(INPUT_GENERATOR_SPECIFICATIONS) >+ > -include $(JS_DOM_HEADERS:.h=.dep) > > # ------------------------ >diff --git a/Source/WebCore/ForwardingHeaders/replay/EncodedValue.h b/Source/WebCore/ForwardingHeaders/replay/EncodedValue.h >new file mode 100644 >index 0000000000000000000000000000000000000000..5e7ce636ca9fd27a8ee095234a2ad515b06ed144 >--- /dev/null >+++ b/Source/WebCore/ForwardingHeaders/replay/EncodedValue.h >@@ -0,0 +1,4 @@ >+#ifndef WebCore_FWD_EncodedValue_h >+#define WebCore_FWD_EncodedValue_h >+#include <JavaScriptCore/EncodedValue.h> >+#endif >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index 51e05bf99973b36cdf93543b0f82394e5a005657..3bf5c7de960aa183595a50dda316cd6479532af5 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -3482,8 +3482,33 @@ > 98CE4326129E00BD005821DC /* LinkLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 98CE4325129E00BD005821DC /* LinkLoader.cpp */; }; > 98CE432A129E00E5005821DC /* LinkLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 98CE4329129E00E5005821DC /* LinkLoader.h */; }; > 98EB1F951313FE0500D0E1EA /* NotImplemented.h in Headers */ = {isa = PBXBuildFile; fileRef = 98EB1F941313FE0500D0E1EA /* NotImplemented.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 990A19FD18ADA48400183FD1 /* AllReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19ED18ADA48400183FD1 /* AllReplayInputs.h */; }; >+ 990A19FE18ADA48400183FD1 /* EventLoopInputDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19EE18ADA48400183FD1 /* EventLoopInputDispatcher.h */; }; >+ 990A1A0118ADA48400183FD1 /* ReplayController.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F118ADA48400183FD1 /* ReplayController.h */; }; >+ 990A1A0218ADA48400183FD1 /* ReplayInputCreationMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A19F218ADA48400183FD1 /* ReplayInputCreationMethods.cpp */; }; >+ 990A1A0318ADA48400183FD1 /* ReplayInputDispatchMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A19F318ADA48400183FD1 /* ReplayInputDispatchMethods.cpp */; }; > 990A1A0418ADA48400183FD1 /* ReplayInputTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A19F418ADA48400183FD1 /* ReplayInputTypes.cpp */; }; > 990A1A0518ADA48400183FD1 /* ReplayInputTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F518ADA48400183FD1 /* ReplayInputTypes.h */; }; >+ 990A1A0618ADA48400183FD1 /* ReplaySession.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F618ADA48400183FD1 /* ReplaySession.h */; }; >+ 990A1A0718ADA48400183FD1 /* ReplaySessionSegment.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F718ADA48400183FD1 /* ReplaySessionSegment.h */; }; >+ 990A1A0818ADA48400183FD1 /* SegmentedInputStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A19F818ADA48400183FD1 /* SegmentedInputStorage.cpp */; }; >+ 990A1A0918ADA48400183FD1 /* SegmentedInputStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F918ADA48400183FD1 /* SegmentedInputStorage.h */; }; >+ 990A1A0A18ADA48400183FD1 /* SerializationMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A19FA18ADA48400183FD1 /* SerializationMethods.cpp */; }; >+ 990A1A0B18ADA48400183FD1 /* SerializationMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19FB18ADA48400183FD1 /* SerializationMethods.h */; }; >+ 990A1A0C18ADA48400183FD1 /* WebInputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 990A19FC18ADA48400183FD1 /* WebInputs.json */; }; >+ 990A1A1118AEDDD400183FD1 /* CapturingInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A1A0F18AEDDD400183FD1 /* CapturingInputCursor.h */; }; >+ 990A1A1218AEDDD400183FD1 /* ReplayingInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A1A1018AEDDD400183FD1 /* ReplayingInputCursor.h */; }; >+ 990A1A1418AEF32400183FD1 /* EventLoopInputDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A1318AEF32400183FD1 /* EventLoopInputDispatcher.cpp */; }; >+ 990A1A1718B3CA8D00183FD1 /* InspectorReplayAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A1518B3CA8D00183FD1 /* InspectorReplayAgent.cpp */; }; >+ 990A1A1818B3CA8E00183FD1 /* InspectorReplayAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A1A1618B3CA8D00183FD1 /* InspectorReplayAgent.h */; }; >+ 990A1A1A18B3DAEC00183FD1 /* FunctorInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A1A1918B3DAEC00183FD1 /* FunctorInputCursor.h */; }; >+ 990A1A1D18B3E26500183FD1 /* WebReplayInputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A1B18B3E26500183FD1 /* WebReplayInputs.cpp */; }; >+ 990A1A1E18B3E26500183FD1 /* WebReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A1A1C18B3E26500183FD1 /* WebReplayInputs.h */; }; >+ 990A1A2018B41A1900183FD1 /* ReplaySessionSegment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A1F18B41A1900183FD1 /* ReplaySessionSegment.cpp */; }; >+ 990A1A2218B44CD800183FD1 /* ReplaySession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A2118B44CD800183FD1 /* ReplaySession.cpp */; }; >+ 990A1A2418B565F400183FD1 /* ReplayController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A2318B565F400183FD1 /* ReplayController.cpp */; }; >+ 990A1A2718B56D7000183FD1 /* CapturingInputCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A2518B56D7000183FD1 /* CapturingInputCursor.cpp */; }; >+ 990A1A2818B56D7000183FD1 /* ReplayingInputCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 990A1A2618B56D7000183FD1 /* ReplayingInputCursor.cpp */; }; > 99E45A1718A063BE0026D88F /* EventLoopInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1618A063BE0026D88F /* EventLoopInput.h */; }; > 9A1142041832D135000BB8AD /* ValueToString.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1142031832D134000BB8AD /* ValueToString.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 9A1B6F97158869C80011A8C4 /* JSDOMStringListCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A1B6F96158869C80011A8C4 /* JSDOMStringListCustom.cpp */; }; >@@ -10435,8 +10460,33 @@ > 98CE4325129E00BD005821DC /* LinkLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LinkLoader.cpp; sourceTree = "<group>"; }; > 98CE4329129E00E5005821DC /* LinkLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinkLoader.h; sourceTree = "<group>"; }; > 98EB1F941313FE0500D0E1EA /* NotImplemented.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotImplemented.h; sourceTree = "<group>"; }; >+ 990A19ED18ADA48400183FD1 /* AllReplayInputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllReplayInputs.h; sourceTree = "<group>"; }; >+ 990A19EE18ADA48400183FD1 /* EventLoopInputDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoopInputDispatcher.h; sourceTree = "<group>"; }; >+ 990A19F118ADA48400183FD1 /* ReplayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayController.h; sourceTree = "<group>"; }; >+ 990A19F218ADA48400183FD1 /* ReplayInputCreationMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayInputCreationMethods.cpp; sourceTree = "<group>"; }; >+ 990A19F318ADA48400183FD1 /* ReplayInputDispatchMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayInputDispatchMethods.cpp; sourceTree = "<group>"; }; > 990A19F418ADA48400183FD1 /* ReplayInputTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayInputTypes.cpp; sourceTree = "<group>"; }; > 990A19F518ADA48400183FD1 /* ReplayInputTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayInputTypes.h; sourceTree = "<group>"; }; >+ 990A19F618ADA48400183FD1 /* ReplaySession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaySession.h; sourceTree = "<group>"; }; >+ 990A19F718ADA48400183FD1 /* ReplaySessionSegment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaySessionSegment.h; sourceTree = "<group>"; }; >+ 990A19F818ADA48400183FD1 /* SegmentedInputStorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentedInputStorage.cpp; sourceTree = "<group>"; }; >+ 990A19F918ADA48400183FD1 /* SegmentedInputStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SegmentedInputStorage.h; sourceTree = "<group>"; }; >+ 990A19FA18ADA48400183FD1 /* SerializationMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerializationMethods.cpp; sourceTree = "<group>"; }; >+ 990A19FB18ADA48400183FD1 /* SerializationMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerializationMethods.h; sourceTree = "<group>"; }; >+ 990A19FC18ADA48400183FD1 /* WebInputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = WebInputs.json; sourceTree = "<group>"; }; >+ 990A1A0F18AEDDD400183FD1 /* CapturingInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CapturingInputCursor.h; sourceTree = "<group>"; }; >+ 990A1A1018AEDDD400183FD1 /* ReplayingInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayingInputCursor.h; sourceTree = "<group>"; }; >+ 990A1A1318AEF32400183FD1 /* EventLoopInputDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoopInputDispatcher.cpp; sourceTree = "<group>"; }; >+ 990A1A1518B3CA8D00183FD1 /* InspectorReplayAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorReplayAgent.cpp; sourceTree = "<group>"; }; >+ 990A1A1618B3CA8D00183FD1 /* InspectorReplayAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorReplayAgent.h; sourceTree = "<group>"; }; >+ 990A1A1918B3DAEC00183FD1 /* FunctorInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FunctorInputCursor.h; sourceTree = "<group>"; }; >+ 990A1A1B18B3E26500183FD1 /* WebReplayInputs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebReplayInputs.cpp; sourceTree = "<group>"; }; >+ 990A1A1C18B3E26500183FD1 /* WebReplayInputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebReplayInputs.h; sourceTree = "<group>"; }; >+ 990A1A1F18B41A1900183FD1 /* ReplaySessionSegment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaySessionSegment.cpp; sourceTree = "<group>"; }; >+ 990A1A2118B44CD800183FD1 /* ReplaySession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaySession.cpp; sourceTree = "<group>"; }; >+ 990A1A2318B565F400183FD1 /* ReplayController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayController.cpp; sourceTree = "<group>"; }; >+ 990A1A2518B56D7000183FD1 /* CapturingInputCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CapturingInputCursor.cpp; sourceTree = "<group>"; }; >+ 990A1A2618B56D7000183FD1 /* ReplayingInputCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayingInputCursor.cpp; sourceTree = "<group>"; }; > 99E45A1618A063BE0026D88F /* EventLoopInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoopInput.h; sourceTree = "<group>"; }; > 9A1142031832D134000BB8AD /* ValueToString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueToString.h; sourceTree = "<group>"; }; > 9A1B6F96158869C80011A8C4 /* JSDOMStringListCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMStringListCustom.cpp; sourceTree = "<group>"; }; >@@ -14846,6 +14896,8 @@ > 4F6FDD631341DEDD001F8EE3 /* InspectorPageAgent.h */, > 9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */, > 9F0D6B2D121BFEBA006C0288 /* InspectorProfilerAgent.h */, >+ 990A1A1518B3CA8D00183FD1 /* InspectorReplayAgent.cpp */, >+ 990A1A1618B3CA8D00183FD1 /* InspectorReplayAgent.h */, > 82AB1771125C826700C5069D /* InspectorResourceAgent.cpp */, > 82AB1772125C826700C5069D /* InspectorResourceAgent.h */, > 82AB176F125C826700C5069D /* InspectorStyleSheet.cpp */, >@@ -15793,6 +15845,8 @@ > 656581AF09D14EE6000E61D7 /* UserAgentStyleSheetsData.cpp */, > 1A1414B313A0F0500019996C /* WebKitFontFamilyNames.cpp */, > 1A1414B413A0F0500019996C /* WebKitFontFamilyNames.h */, >+ 990A1A1B18B3E26500183FD1 /* WebReplayInputs.cpp */, >+ 990A1A1C18B3E26500183FD1 /* WebReplayInputs.h */, > 656581EA09D1508D000E61D7 /* XLinkNames.cpp */, > 656581EB09D1508D000E61D7 /* XLinkNames.h */, > A833C80A0A2CF25600D57664 /* XMLNames.cpp */, >@@ -17838,9 +17892,30 @@ > 99E45A1318A021760026D88F /* replay */ = { > isa = PBXGroup; > children = ( >+ 990A19ED18ADA48400183FD1 /* AllReplayInputs.h */, >+ 990A1A2518B56D7000183FD1 /* CapturingInputCursor.cpp */, >+ 990A1A0F18AEDDD400183FD1 /* CapturingInputCursor.h */, > 99E45A1618A063BE0026D88F /* EventLoopInput.h */, >+ 990A1A1318AEF32400183FD1 /* EventLoopInputDispatcher.cpp */, >+ 990A19EE18ADA48400183FD1 /* EventLoopInputDispatcher.h */, >+ 990A1A1918B3DAEC00183FD1 /* FunctorInputCursor.h */, >+ 990A1A2318B565F400183FD1 /* ReplayController.cpp */, >+ 990A19F118ADA48400183FD1 /* ReplayController.h */, >+ 990A1A2618B56D7000183FD1 /* ReplayingInputCursor.cpp */, >+ 990A1A1018AEDDD400183FD1 /* ReplayingInputCursor.h */, >+ 990A19F218ADA48400183FD1 /* ReplayInputCreationMethods.cpp */, >+ 990A19F318ADA48400183FD1 /* ReplayInputDispatchMethods.cpp */, > 990A19F418ADA48400183FD1 /* ReplayInputTypes.cpp */, > 990A19F518ADA48400183FD1 /* ReplayInputTypes.h */, >+ 990A1A2118B44CD800183FD1 /* ReplaySession.cpp */, >+ 990A19F618ADA48400183FD1 /* ReplaySession.h */, >+ 990A1A1F18B41A1900183FD1 /* ReplaySessionSegment.cpp */, >+ 990A19F718ADA48400183FD1 /* ReplaySessionSegment.h */, >+ 990A19F818ADA48400183FD1 /* SegmentedInputStorage.cpp */, >+ 990A19F918ADA48400183FD1 /* SegmentedInputStorage.h */, >+ 990A19FA18ADA48400183FD1 /* SerializationMethods.cpp */, >+ 990A19FB18ADA48400183FD1 /* SerializationMethods.h */, >+ 990A19FC18ADA48400183FD1 /* WebInputs.json */, > ); > path = replay; > sourceTree = "<group>"; >@@ -22684,6 +22759,7 @@ > E1CDE9221501916900862CC5 /* AsyncFileStream.h in Headers */, > 26C15CF71857E15E00F15C03 /* ResourceHandleCFURLConnectionDelegateWithOperationQueue.h in Headers */, > 7C5343FD17B74B63004232F0 /* JSMediaQueryListListener.h in Headers */, >+ 990A1A0918ADA48400183FD1 /* SegmentedInputStorage.h in Headers */, > 37C61F0112095C87007A3C67 /* AtomicStringKeyedMRUCache.h in Headers */, > E1FF8F601807442100132674 /* SubtleCrypto.h in Headers */, > A8C4A80D09D563270003AC8D /* Attr.h in Headers */, >@@ -23474,6 +23550,7 @@ > 97B38E27151C4271004622E9 /* DOMWindowNotifications.h in Headers */, > 51ABB5B9186D0ED1008391A1 /* IDBObjectStoreMetadata.h in Headers */, > 97D2AD0414B823A60093DF32 /* DOMWindowProperty.h in Headers */, >+ 990A19FE18ADA48400183FD1 /* EventLoopInputDispatcher.h in Headers */, > 0779BF0E18453168000B6AE7 /* HTMLMediaElementMediaStream.h in Headers */, > 89F60B11157F686E0075E157 /* DOMWindowQuota.h in Headers */, > AA2A5AD616A4861600975A25 /* DOMWindowSpeechSynthesis.h in Headers */, >@@ -23495,6 +23572,7 @@ > 81F65FF613788FAA00FF6F2D /* DragState.h in Headers */, > 498770DC1242C535002226BA /* DrawingBuffer.h in Headers */, > E1BA66F11742BD8600C20251 /* DynamicLinkerInterposing.h in Headers */, >+ 990A1A1E18B3E26500183FD1 /* WebReplayInputs.h in Headers */, > FD6ED2C8136B8E66003CF072 /* DynamicsCompressor.h in Headers */, > FD537357137B653B00008DCE /* DynamicsCompressorKernel.h in Headers */, > FD6ED2C4136B8E42003CF072 /* DynamicsCompressorNode.h in Headers */, >@@ -23567,6 +23645,7 @@ > 078E092517D14D1C00420AA1 /* RTCPeerConnection.h in Headers */, > 84801955108BAFB300CB2B1F /* FEGaussianBlur.h in Headers */, > 84730D811248F0B300D3A9C9 /* FELighting.h in Headers */, >+ 990A1A0118ADA48400183FD1 /* ReplayController.h in Headers */, > 84730D831248F0B300D3A9C9 /* FEMerge.h in Headers */, > 84730D851248F0B300D3A9C9 /* FEMorphology.h in Headers */, > 84730D871248F0B300D3A9C9 /* FEOffset.h in Headers */, >@@ -23722,6 +23801,7 @@ > 930908910AF7EDE40081DF01 /* HitTestRequest.h in Headers */, > 9307F1D80AF2D59000DBA31A /* HitTestResult.h in Headers */, > A14832CF187F684700DA63A6 /* WebCoreThreadSystemInterface.h in Headers */, >+ 990A1A1218AEDDD400183FD1 /* ReplayingInputCursor.h in Headers */, > BC3BC29C0E91AB0F00835588 /* HostWindow.h in Headers */, > FD31609912B026F700C1A359 /* HRTFDatabase.h in Headers */, > FD31609B12B026F700C1A359 /* HRTFDatabaseLoader.h in Headers */, >@@ -23784,6 +23864,7 @@ > A871DE2A0A152AC800B12A68 /* HTMLIFrameElement.h in Headers */, > A8EA7D2D0A19385500A8EF5F /* HTMLImageElement.h in Headers */, > A8EA7D2B0A19385500A8EF5F /* HTMLImageLoader.h in Headers */, >+ 990A1A1818B3CA8E00183FD1 /* InspectorReplayAgent.h in Headers */, > A81369CC097374F600D74463 /* HTMLInputElement.h in Headers */, > 0FC4E410187F82E10045882C /* ScrollingTreeIOS.h in Headers */, > 97BC849B12370A4B000C6161 /* HTMLInputStream.h in Headers */, >@@ -23880,6 +23961,7 @@ > C0C054CD1118C8E400CE2636 /* IDLParser.pm in Headers */, > C3CF17A515B0063F00276D39 /* IdTargetObserver.h in Headers */, > C3CF17A715B0063F00276D39 /* IdTargetObserverRegistry.h in Headers */, >+ 990A19FD18ADA48400183FD1 /* AllReplayInputs.h in Headers */, > 8AB4BC77126FDB7100DEB727 /* IgnoreDestructiveWriteCountIncrementer.h in Headers */, > B27535700B053814002CE64F /* Image.h in Headers */, > B2A10B920B3818BD00099AA4 /* ImageBuffer.h in Headers */, >@@ -24267,6 +24349,7 @@ > 8A9A587511E84C81008ACFD1 /* JSPerformance.h in Headers */, > 8A9A587111E84C36008ACFD1 /* JSPerformanceNavigation.h in Headers */, > 8A9A588811E84F37008ACFD1 /* JSPerformanceTiming.h in Headers */, >+ 990A1A0718ADA48400183FD1 /* ReplaySessionSegment.h in Headers */, > FDEA6247152102FC00479DF0 /* JSPeriodicWave.h in Headers */, > 93B70D6C09EB0C7C009D8468 /* JSPluginElementFunctions.h in Headers */, > 5189F01E10B37BD900F3C739 /* JSPopStateEvent.h in Headers */, >@@ -24424,6 +24507,7 @@ > B2FA3DCB0AB75A6F000E5AC4 /* JSSVGPathSegCurvetoQuadraticSmoothAbs.h in Headers */, > B2FA3DCD0AB75A6F000E5AC4 /* JSSVGPathSegCurvetoQuadraticSmoothRel.h in Headers */, > B2FA3DCF0AB75A6F000E5AC4 /* JSSVGPathSegLinetoAbs.h in Headers */, >+ 990A1A0B18ADA48400183FD1 /* SerializationMethods.h in Headers */, > B2FA3DD10AB75A6F000E5AC4 /* JSSVGPathSegLinetoHorizontalAbs.h in Headers */, > B2FA3DD30AB75A6F000E5AC4 /* JSSVGPathSegLinetoHorizontalRel.h in Headers */, > 51660AB91828BB8200074CAD /* IDBFactoryBackendLevelDB.h in Headers */, >@@ -24446,6 +24530,7 @@ > B2FA3DEA0AB75A6F000E5AC4 /* JSSVGPreserveAspectRatio.h in Headers */, > B2FA3DEC0AB75A6F000E5AC4 /* JSSVGRadialGradientElement.h in Headers */, > 8542A7930AE5C93F00DF58DF /* JSSVGRect.h in Headers */, >+ 990A1A0618ADA48400183FD1 /* ReplaySession.h in Headers */, > B2FA3DEE0AB75A6F000E5AC4 /* JSSVGRectElement.h in Headers */, > B2FA3DF10AB75A6F000E5AC4 /* JSSVGRenderingIntent.h in Headers */, > B2FA3DF30AB75A6F000E5AC4 /* JSSVGScriptElement.h in Headers */, >@@ -24803,6 +24888,7 @@ > 51889F0C1554AF3D004427F7 /* PDFDocument.h in Headers */, > B27535650B053814002CE64F /* PDFDocumentImage.h in Headers */, > 8A7CC96B12076D73001D4588 /* PendingScript.h in Headers */, >+ 990A1A1A18B3DAEC00183FD1 /* FunctorInputCursor.h in Headers */, > 070363E6181A1CDC00C074A5 /* AVVideoCaptureSource.h in Headers */, > 8A844D0511D3C18E0014065C /* Performance.h in Headers */, > 07969DB017D14151007FF842 /* JSRTCDTMFSender.h in Headers */, >@@ -25904,6 +25990,7 @@ > 073BE34D17D17E7A002BD431 /* JSNavigatorUserMediaSuccessCallback.h in Headers */, > 1AB7FC690A8B92EC00D9D37B /* XPathEvaluator.h in Headers */, > BC60DA5B0D2A31F700B9918F /* XPathException.h in Headers */, >+ 990A1A1118AEDDD400183FD1 /* CapturingInputCursor.h in Headers */, > 1AB7FC6C0A8B92EC00D9D37B /* XPathExpression.h in Headers */, > 1AB7FC6F0A8B92EC00D9D37B /* XPathExpressionNode.h in Headers */, > 1AB7FC710A8B92EC00D9D37B /* XPathFunctions.h in Headers */, >@@ -26127,6 +26214,7 @@ > 46F9D5DF0B0D60170028EE36 /* progressCursor.png in Resources */, > 85136CA40AED665900F90A3D /* southEastResizeCursor.png in Resources */, > 85136CA50AED665900F90A3D /* southResizeCursor.png in Resources */, >+ 990A1A0C18ADA48400183FD1 /* WebInputs.json in Resources */, > 85136CA60AED665900F90A3D /* southWestResizeCursor.png in Resources */, > 93153BDC141959BC00FCF5BE /* textAreaResizeCorner.png in Resources */, > 9370918D1416D86B00477333 /* textAreaResizeCorner@2x.png in Resources */, >@@ -26245,7 +26333,7 @@ > ); > runOnlyForDeploymentPostprocessing = 0; > shellPath = /bin/sh; >- shellScript = "mkdir -p \"${BUILT_PRODUCTS_DIR}/DerivedSources/WebCore\"\ncd \"${BUILT_PRODUCTS_DIR}/DerivedSources/WebCore\"\n\n/bin/ln -sfh \"${SRCROOT}\" WebCore\nexport WebCore=\"WebCore\"\nexport InspectorScripts=\"${JAVASCRIPTCORE_PRIVATE_HEADERS_DIR}\"\n\nif [ ! $CC ]; then\n export CC=\"`xcrun -find clang`\"\nfi\n\nif [ ! $GPERF ]; then\n export GPERF=\"`xcrun -find gperf`\"\nfi\n\nif [ \"${ACTION}\" = \"build\" -o \"${ACTION}\" = \"install\" -o \"${ACTION}\" = \"installhdrs\" ]; then\n make --no-builtin-rules -f \"WebCore/DerivedSources.make\" -j `/usr/sbin/sysctl -n hw.activecpu` SDKROOT=\"${SDKROOT}\"\nfi\n"; >+ shellScript = "mkdir -p \"${BUILT_PRODUCTS_DIR}/DerivedSources/WebCore\"\ncd \"${BUILT_PRODUCTS_DIR}/DerivedSources/WebCore\"\n\n/bin/ln -sfh \"${SRCROOT}\" WebCore\nexport WebCore=\"WebCore\"\nexport InspectorScripts=\"${JAVASCRIPTCORE_PRIVATE_HEADERS_DIR}\"\nexport WebReplayScripts=\"${JAVASCRIPTCORE_PRIVATE_HEADERS_DIR}\"\n\nif [ ! $CC ]; then\n export CC=\"`xcrun -find clang`\"\nfi\n\nif [ ! $GPERF ]; then\n export GPERF=\"`xcrun -find gperf`\"\nfi\n\nif [ \"${ACTION}\" = \"build\" -o \"${ACTION}\" = \"install\" -o \"${ACTION}\" = \"installhdrs\" ]; then\n make --no-builtin-rules -f \"WebCore/DerivedSources.make\" -j `/usr/sbin/sysctl -n hw.activecpu` SDKROOT=\"${SDKROOT}\"\nfi\n"; > }; > /* End PBXShellScriptBuildPhase section */ > >@@ -26345,6 +26433,7 @@ > FD31601712B0267600C1A359 /* AudioNodeOutput.cpp in Sources */, > FD31601C12B0267600C1A359 /* AudioParam.cpp in Sources */, > FD35918F138DB22000E1EBEC /* AudioParamTimeline.cpp in Sources */, >+ 990A1A1418AEF32400183FD1 /* EventLoopInputDispatcher.cpp in Sources */, > FD31601F12B0267600C1A359 /* AudioProcessingEvent.cpp in Sources */, > FD31608512B026F700C1A359 /* AudioResampler.cpp in Sources */, > FD31608712B026F700C1A359 /* AudioResamplerKernel.cpp in Sources */, >@@ -26394,6 +26483,7 @@ > 377A3A9015EFCE9B0059F5C7 /* BitmapImageCG.cpp in Sources */, > 976D6C78122B8A3D001FD1F7 /* Blob.cpp in Sources */, > 2EDEF1F3121B0EFC00726DB2 /* BlobData.cpp in Sources */, >+ 990A1A0218ADA48400183FD1 /* ReplayInputCreationMethods.cpp in Sources */, > E14A94D716DFDF950068DE82 /* BlobRegistry.cpp in Sources */, > 2EDEF1F6121B0EFC00726DB2 /* BlobRegistryImpl.cpp in Sources */, > 2EB4BCD2121F03E300EC4885 /* BlobResourceHandle.cpp in Sources */, >@@ -26717,6 +26807,7 @@ > BC1BDF24156C1883001C1243 /* DOMError.cpp in Sources */, > 85AFA8230AAF528A00E84305 /* DOMEvent.mm in Sources */, > BC1A37B3097C715F0019F3D8 /* DOMEvents.mm in Sources */, >+ 990A1A0318ADA48400183FD1 /* ReplayInputDispatchMethods.cpp in Sources */, > BC00F0050E0A185500FD04E3 /* DOMFile.mm in Sources */, > 2E3BC109117D479800B9409A /* DOMFileError.mm in Sources */, > BC00F0080E0A185500FD04E3 /* DOMFileList.mm in Sources */, >@@ -26751,6 +26842,7 @@ > 85C050BB0AD84F5E005532E7 /* DOMHTMLEmbedElement.mm in Sources */, > 85BA4CE00AA6861B0088052D /* DOMHTMLFieldSetElement.mm in Sources */, > 85ECBEF00AA7626900544F0B /* DOMHTMLFontElement.mm in Sources */, >+ 990A1A2718B56D7000183FD1 /* CapturingInputCursor.cpp in Sources */, > 85DF2C5D0AA341F600AD64C5 /* DOMHTMLFormElement.mm in Sources */, > 85DF81960AA77E4B00486AD7 /* DOMHTMLFrameElement.mm in Sources */, > 85DF81980AA77E4B00486AD7 /* DOMHTMLFrameSetElement.mm in Sources */, >@@ -27240,6 +27332,7 @@ > 07941791166E855F009416C2 /* InbandTextTrack.cpp in Sources */, > 07B442D6166C70B000556CAD /* InbandTextTrackPrivateAVF.cpp in Sources */, > 07AA6B6B166D2C0700D45671 /* InbandTextTrackPrivateAVFObjC.mm in Sources */, >+ 990A1A0818ADA48400183FD1 /* SegmentedInputStorage.cpp in Sources */, > 07367DE0172CA68200D861B9 /* InbandTextTrackPrivateLegacyAVFObjC.mm in Sources */, > DB23C2CB0A508D29002489EB /* IndentOutdentCommand.cpp in Sources */, > AA73183E159255B900A93E6E /* InjectedScriptCanvasModule.cpp in Sources */, >@@ -27524,6 +27617,7 @@ > BCD41ABB0C060DE800C0E446 /* JSHTMLFrameSetElementCustom.cpp in Sources */, > A80E7B140A19D606007FB8C5 /* JSHTMLHeadElement.cpp in Sources */, > 1AE2AA2E0A1CDAB400B42B25 /* JSHTMLHeadingElement.cpp in Sources */, >+ 990A1A2818B56D7000183FD1 /* ReplayingInputCursor.cpp in Sources */, > 1AE2AA300A1CDAB400B42B25 /* JSHTMLHRElement.cpp in Sources */, > 1A85B18F0A1B18A200D8C87C /* JSHTMLHtmlElement.cpp in Sources */, > 0705853817FE044F005F2BCB /* MediaTrackConstraintSet.cpp in Sources */, >@@ -27596,6 +27690,7 @@ > CD27F6E51457685A0078207D /* JSMediaController.cpp in Sources */, > FD23A12513F5FA5900F67001 /* JSMediaElementAudioSourceNode.cpp in Sources */, > E44614180CD6826900FADA75 /* JSMediaError.cpp in Sources */, >+ 990A1A2018B41A1900183FD1 /* ReplaySessionSegment.cpp in Sources */, > E19AC3F41824DC7900349426 /* CryptoAlgorithmSHA512.cpp in Sources */, > 510B7DB7182D9F5D009BF957 /* IDBTransactionBackend.cpp in Sources */, > A14832CB187F67B400DA63A6 /* WebCoreThreadRun.cpp in Sources */, >@@ -27740,6 +27835,7 @@ > 51660AC01828C84600074CAD /* IDBBackingStoreCursorLevelDB.cpp in Sources */, > B2FA3D560AB75A6F000E5AC4 /* JSSVGAnimationElement.cpp in Sources */, > B2FA3D580AB75A6F000E5AC4 /* JSSVGCircleElement.cpp in Sources */, >+ 990A1A2218B44CD800183FD1 /* ReplaySession.cpp in Sources */, > B2FA3D5A0AB75A6F000E5AC4 /* JSSVGClipPathElement.cpp in Sources */, > B2FA3D5C0AB75A6F000E5AC4 /* JSSVGColor.cpp in Sources */, > B2FA3D5E0AB75A6F000E5AC4 /* JSSVGComponentTransferFunctionElement.cpp in Sources */, >@@ -27764,6 +27860,7 @@ > B2FA3D720AB75A6F000E5AC4 /* JSSVGFEDiffuseLightingElement.cpp in Sources */, > B2FA3D740AB75A6F000E5AC4 /* JSSVGFEDisplacementMapElement.cpp in Sources */, > 1F72BF0A187FD4490009BCB3 /* TileControllerMemoryHandlerIOS.cpp in Sources */, >+ 990A1A1D18B3E26500183FD1 /* WebReplayInputs.cpp in Sources */, > B2FA3D760AB75A6F000E5AC4 /* JSSVGFEDistantLightElement.cpp in Sources */, > 4358E87C1360A2EE00E4748C /* JSSVGFEDropShadowElement.cpp in Sources */, > B2FA3D780AB75A6F000E5AC4 /* JSSVGFEFloodElement.cpp in Sources */, >@@ -28045,6 +28142,7 @@ > FA654A6B1108ABED002615E0 /* MathMLTextElement.cpp in Sources */, > E45390450EAFD637003695C8 /* ScrollViewIOS.mm in Sources */, > 49D5DC2B0F423A73008F20FD /* Matrix3DTransformOperation.cpp in Sources */, >+ 990A1A1718B3CA8D00183FD1 /* InspectorReplayAgent.cpp in Sources */, > 49E911C60EF86D47009D0CAF /* MatrixTransformOperation.cpp in Sources */, > 417253AA1354BBBC00360F2A /* MediaControlElements.cpp in Sources */, > DEBCCDD516646EB200A452E1 /* MediaControlElementTypes.cpp in Sources */, >@@ -28543,6 +28641,7 @@ > 93309E11099E64920056E581 /* SplitElementCommand.cpp in Sources */, > 93309E13099E64920056E581 /* SplitTextNodeCommand.cpp in Sources */, > 078E094B17D1709600420AA1 /* MediaStreamAudioDestinationNode.cpp in Sources */, >+ 990A1A0A18ADA48400183FD1 /* SerializationMethods.cpp in Sources */, > 070E091B1875EF71003A1D3C /* MediaSession.cpp in Sources */, > 93309E15099E64920056E581 /* SplitTextNodeContainingElementCommand.cpp in Sources */, > A1E1154813015C5D0054AC8C /* SpotLightSource.cpp in Sources */, >@@ -28652,6 +28751,7 @@ > 4381763B13A697D4007D1187 /* SVGAnimatedLength.cpp in Sources */, > 431A2FD713B7707A007791E4 /* SVGAnimatedLengthList.cpp in Sources */, > 43A0F0B113AC7D6D00A5F0A7 /* SVGAnimatedNumber.cpp in Sources */, >+ 990A1A2418B565F400183FD1 /* ReplayController.cpp in Sources */, > 073BE34817D17E7A002BD431 /* JSNavigatorUserMediaError.cpp in Sources */, > 4331AC7813B6870000A9E5AE /* SVGAnimatedNumberList.cpp in Sources */, > 431A2F9D13B6F2B0007791E4 /* SVGAnimatedNumberOptionalNumber.cpp in Sources */, >diff --git a/Source/WebCore/inspector/InspectorController.cpp b/Source/WebCore/inspector/InspectorController.cpp >index 835ff4c93050d5940e16953642d94e2b4f2dd85f..3bb80d2071bf2efca338253266a4d452135ebc22 100644 >--- a/Source/WebCore/inspector/InspectorController.cpp >+++ b/Source/WebCore/inspector/InspectorController.cpp >@@ -1,5 +1,6 @@ > /* > * Copyright (C) 2011 Google Inc. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions are >@@ -54,6 +55,7 @@ > #include "InspectorOverlay.h" > #include "InspectorPageAgent.h" > #include "InspectorProfilerAgent.h" >+#include "InspectorReplayAgent.h" > #include "InspectorResourceAgent.h" > #include "InspectorTimelineAgent.h" > #include "InspectorWebBackendDispatchers.h" >@@ -126,6 +128,10 @@ InspectorController::InspectorController(Page& page, InspectorClient* inspectorC > m_agents.append(std::make_unique<InspectorIndexedDBAgent>(m_instrumentingAgents.get(), m_injectedScriptManager.get(), pageAgent)); > #endif > >+#if ENABLE(WEB_REPLAY) >+ m_agents.append(std::make_unique<InspectorReplayAgent>(m_instrumentingAgents.get(), pageAgent)); >+#endif >+ > auto domStorageAgentPtr = std::make_unique<InspectorDOMStorageAgent>(m_instrumentingAgents.get(), m_pageAgent); > InspectorDOMStorageAgent* domStorageAgent = domStorageAgentPtr.get(); > m_agents.append(std::move(domStorageAgentPtr)); >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.cpp b/Source/WebCore/inspector/InspectorInstrumentation.cpp >index 30512ab3b5de44c1d3fca577853b2efa05f20d3e..f53c287d326ae0da487b33da3f405cab39c92bb9 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.cpp >+++ b/Source/WebCore/inspector/InspectorInstrumentation.cpp >@@ -1,5 +1,6 @@ > /* > * Copyright (C) 2011 Google Inc. All rights reserved. >+* Copyright (C) 2014 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions are >@@ -77,6 +78,11 @@ > #include <wtf/StdLibExtras.h> > #include <wtf/text/CString.h> > >+#if ENABLE(WEB_REPLAY) >+#include "InspectorReplayAgent.h" >+#include "ReplayController.h" // for ReplayPosition. >+#endif >+ > using namespace Inspector; > > namespace WebCore { >@@ -743,6 +749,13 @@ void InspectorInstrumentation::frameDetachedFromParentImpl(InstrumentingAgents* > canvasAgent->frameDetached(frame); > if (InspectorPageAgent* pageAgent = instrumentingAgents->inspectorPageAgent()) > pageAgent->frameDetached(frame); >+ >+#if ENABLE(WEB_REPLAY) >+ if (frame->isMainFrame()) { >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->frameDetached(frame); >+ } >+#endif > } > > void InspectorInstrumentation::didCommitLoadImpl(InstrumentingAgents* instrumentingAgents, Page* page, DocumentLoader* loader) >@@ -777,6 +790,10 @@ void InspectorInstrumentation::didCommitLoadImpl(InstrumentingAgents* instrument > canvasAgent->frameNavigated(loader->frame()); > if (InspectorPageAgent* pageAgent = instrumentingAgents->inspectorPageAgent()) > pageAgent->frameNavigated(loader); >+#if ENABLE(WEB_REPLAY) >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->frameNavigated(loader); >+#endif > } > > void InspectorInstrumentation::frameDocumentUpdatedImpl(InstrumentingAgents* instrumentingAgents, Frame* frame) >@@ -1050,6 +1067,80 @@ void InspectorInstrumentation::didSendWebSocketFrameImpl(InstrumentingAgents* in > } > #endif > >+#if ENABLE(WEB_REPLAY) >+void InspectorInstrumentation::sessionCreatedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySession> session) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->sessionCreated(session); >+} >+ >+void InspectorInstrumentation::sessionLoadedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySession> session) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->sessionLoaded(session); >+} >+ >+void InspectorInstrumentation::sessionModifiedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySession> session) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->sessionModified(session); >+} >+ >+void InspectorInstrumentation::segmentCreatedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySessionSegment> segment) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->segmentCreated(segment); >+} >+ >+void InspectorInstrumentation::segmentCompletedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySessionSegment> segment) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->segmentCompleted(segment); >+} >+ >+void InspectorInstrumentation::segmentLoadedImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ReplaySessionSegment> segment) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->segmentLoaded(segment); >+} >+ >+void InspectorInstrumentation::segmentUnloadedImpl(InstrumentingAgents* instrumentingAgents) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->segmentUnloaded(); >+} >+ >+void InspectorInstrumentation::captureStartedImpl(InstrumentingAgents* instrumentingAgents) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->captureStarted(); >+} >+ >+void InspectorInstrumentation::captureStoppedImpl(InstrumentingAgents* instrumentingAgents) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->captureStopped(); >+} >+ >+void InspectorInstrumentation::playbackStartedImpl(InstrumentingAgents* instrumentingAgents) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->playbackStarted(); >+} >+ >+void InspectorInstrumentation::playbackPausedImpl(InstrumentingAgents* instrumentingAgents, const ReplayPosition& position) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->playbackPaused(position); >+} >+ >+void InspectorInstrumentation::playbackHitPositionImpl(InstrumentingAgents* instrumentingAgents, const ReplayPosition& position) >+{ >+ if (InspectorReplayAgent* replayAgent = instrumentingAgents->inspectorReplayAgent()) >+ replayAgent->playbackHitPosition(position); >+} >+#endif >+ > void InspectorInstrumentation::networkStateChangedImpl(InstrumentingAgents* instrumentingAgents) > { > if (InspectorApplicationCacheAgent* applicationCacheAgent = instrumentingAgents->inspectorApplicationCacheAgent()) >@@ -1081,6 +1172,17 @@ bool InspectorInstrumentation::timelineAgentEnabled(ScriptExecutionContext* scri > return instrumentingAgents && instrumentingAgents->inspectorTimelineAgent(); > } > >+bool InspectorInstrumentation::replayAgentEnabled(ScriptExecutionContext* scriptExecutionContext) >+{ >+#if ENABLE(WEB_REPLAY) >+ InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(scriptExecutionContext); >+ return instrumentingAgents && instrumentingAgents->inspectorReplayAgent(); >+#else >+ UNUSED_PARAM(scriptExecutionContext); >+ return false; >+#endif >+} >+ > void InspectorInstrumentation::pauseOnNativeEventIfNeeded(InstrumentingAgents* instrumentingAgents, bool isDOMEvent, const String& eventName, bool synchronous) > { > if (InspectorDOMDebuggerAgent* domDebuggerAgent = instrumentingAgents->inspectorDOMDebuggerAgent()) >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.h b/Source/WebCore/inspector/InspectorInstrumentation.h >index ddff892ee5cab1cd6369d0d5d546b283b76575a5..7b94c0a902a2e5ec883ac7cc4c45068735993127 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.h >+++ b/Source/WebCore/inspector/InspectorInstrumentation.h >@@ -1,5 +1,6 @@ > /* > * Copyright (C) 2010 Google Inc. All rights reserved. >+* Copyright (C) 2014 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions are >@@ -45,6 +46,12 @@ > #include <inspector/ConsoleTypes.h> > #include <wtf/RefPtr.h> > >+#if ENABLE(WEB_REPLAY) >+#include "ReplaySession.h" >+#include "ReplaySessionSegment.h" >+#endif >+ >+ > namespace Deprecated { > class ScriptObject; > } >@@ -93,6 +100,8 @@ class WorkerGlobalScope; > class WorkerGlobalScopeProxy; > class XMLHttpRequest; > >+struct ReplayPosition; >+ > #define FAST_RETURN_IF_NO_FRONTENDS(value) if (LIKELY(!hasFrontends())) return value; > > class InspectorInstrumentation { >@@ -239,6 +248,24 @@ public: > static void workerGlobalScopeTerminated(ScriptExecutionContext*, WorkerGlobalScopeProxy*); > static void willEvaluateWorkerScript(WorkerGlobalScope*, int workerThreadStartMode); > >+#if ENABLE(WEB_REPLAY) >+ static void sessionCreated(Page*, PassRefPtr<ReplaySession>); >+ static void sessionLoaded(Page*, PassRefPtr<ReplaySession>); >+ static void sessionModified(Page*, PassRefPtr<ReplaySession>); >+ >+ static void segmentCreated(Page*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentCompleted(Page*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentLoaded(Page*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentUnloaded(Page*); >+ >+ static void captureStarted(Page*); >+ static void captureStopped(Page*); >+ >+ static void playbackStarted(Page*); >+ static void playbackPaused(Page*, const ReplayPosition&); >+ static void playbackHitPosition(Page*, const ReplayPosition&); >+#endif >+ > #if ENABLE(WEB_SOCKETS) > static void didCreateWebSocket(Document*, unsigned long identifier, const URL& requestURL, const URL& documentURL, const String& protocol); > static void willSendWebSocketHandshakeRequest(Document*, unsigned long identifier, const ResourceRequest&); >@@ -264,12 +291,14 @@ public: > static bool canvasAgentEnabled(ScriptExecutionContext*); > static bool consoleAgentEnabled(ScriptExecutionContext*); > static bool timelineAgentEnabled(ScriptExecutionContext*); >+ static bool replayAgentEnabled(ScriptExecutionContext*); > #else > static bool hasFrontends() { return false; } > static bool canvasAgentEnabled(ScriptExecutionContext*) { return false; } > static bool consoleAgentEnabled(ScriptExecutionContext*) { return false; } > static bool runtimeAgentEnabled(Frame*) { return false; } > static bool timelineAgentEnabled(ScriptExecutionContext*) { return false; } >+ static bool replayAgentEnabled(ScriptExecutionContext*) { return false; } > #endif > > static void registerInstrumentingAgents(InstrumentingAgents*); >@@ -418,6 +447,24 @@ private: > static void didStartWorkerGlobalScopeImpl(InstrumentingAgents*, WorkerGlobalScopeProxy*, const URL&); > static void workerGlobalScopeTerminatedImpl(InstrumentingAgents*, WorkerGlobalScopeProxy*); > >+#if ENABLE(WEB_REPLAY) >+ static void sessionCreatedImpl(InstrumentingAgents*, PassRefPtr<ReplaySession>); >+ static void sessionLoadedImpl(InstrumentingAgents*, PassRefPtr<ReplaySession>); >+ static void sessionModifiedImpl(InstrumentingAgents*, PassRefPtr<ReplaySession>); >+ >+ static void segmentCreatedImpl(InstrumentingAgents*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentCompletedImpl(InstrumentingAgents*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentLoadedImpl(InstrumentingAgents*, PassRefPtr<ReplaySessionSegment>); >+ static void segmentUnloadedImpl(InstrumentingAgents*); >+ >+ static void captureStartedImpl(InstrumentingAgents*); >+ static void captureStoppedImpl(InstrumentingAgents*); >+ >+ static void playbackStartedImpl(InstrumentingAgents*); >+ static void playbackPausedImpl(InstrumentingAgents*, const ReplayPosition&); >+ static void playbackHitPositionImpl(InstrumentingAgents*, const ReplayPosition&); >+#endif >+ > #if ENABLE(WEB_SOCKETS) > static void didCreateWebSocketImpl(InstrumentingAgents*, unsigned long identifier, const URL& requestURL, const URL& documentURL, const String& protocol, Document*); > static void willSendWebSocketHandshakeRequestImpl(InstrumentingAgents*, unsigned long identifier, const ResourceRequest&, Document*); >@@ -1775,6 +1822,136 @@ inline void InspectorInstrumentation::didSendWebSocketFrame(Document* document, > } > #endif > >+#if ENABLE(WEB_REPLAY) >+inline void InspectorInstrumentation::sessionCreated(Page* page, PassRefPtr<ReplaySession> session) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ sessionCreatedImpl(instrumentingAgents, session); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(session); >+#endif >+} >+ >+inline void InspectorInstrumentation::sessionLoaded(Page* page, PassRefPtr<ReplaySession> session) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ sessionLoadedImpl(instrumentingAgents, session); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(session); >+#endif >+} >+ >+inline void InspectorInstrumentation::sessionModified(Page* page, PassRefPtr<ReplaySession> session) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ sessionModifiedImpl(instrumentingAgents, session); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(session); >+#endif >+} >+ >+inline void InspectorInstrumentation::segmentCreated(Page* page, PassRefPtr<ReplaySessionSegment> segment) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ segmentCreatedImpl(instrumentingAgents, segment); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(segment); >+#endif >+} >+ >+inline void InspectorInstrumentation::segmentCompleted(Page* page, PassRefPtr<ReplaySessionSegment> segment) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ segmentCompletedImpl(instrumentingAgents, segment); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(segment); >+#endif >+} >+ >+inline void InspectorInstrumentation::segmentLoaded(Page* page, PassRefPtr<ReplaySessionSegment> segment) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ segmentLoadedImpl(instrumentingAgents, segment); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(segment); >+#endif >+} >+ >+inline void InspectorInstrumentation::segmentUnloaded(Page* page) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ segmentUnloadedImpl(instrumentingAgents); >+#else >+ UNUSED_PARAM(page); >+#endif >+} >+ >+inline void InspectorInstrumentation::captureStarted(Page* page) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ captureStartedImpl(instrumentingAgents); >+#else >+ UNUSED_PARAM(page); >+#endif >+} >+ >+inline void InspectorInstrumentation::captureStopped(Page* page) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ captureStoppedImpl(instrumentingAgents); >+#else >+ UNUSED_PARAM(page); >+#endif >+} >+ >+inline void InspectorInstrumentation::playbackStarted(Page* page) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ playbackStartedImpl(instrumentingAgents); >+#else >+ UNUSED_PARAM(page); >+#endif >+} >+ >+inline void InspectorInstrumentation::playbackPaused(Page* page, const ReplayPosition& position) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ playbackPausedImpl(instrumentingAgents, position); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(position); >+#endif >+} >+ >+inline void InspectorInstrumentation::playbackHitPosition(Page* page, const ReplayPosition& position) >+{ >+#if ENABLE(INSPECTOR) >+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) >+ playbackHitPositionImpl(instrumentingAgents, position); >+#else >+ UNUSED_PARAM(page); >+ UNUSED_PARAM(position); >+#endif >+} >+#endif // ENABLE(WEB_REPLAY) >+ > inline void InspectorInstrumentation::networkStateChanged(Page* page) > { > #if ENABLE(INSPECTOR) >diff --git a/Source/WebCore/inspector/InspectorReplayAgent.cpp b/Source/WebCore/inspector/InspectorReplayAgent.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..37a218544acd72b2172539efe547d819ed054cea >--- /dev/null >+++ b/Source/WebCore/inspector/InspectorReplayAgent.cpp >@@ -0,0 +1,504 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "InspectorReplayAgent.h" >+ >+#if ENABLE(INSPECTOR) && ENABLE(WEB_REPLAY) >+ >+#include "DocumentLoader.h" >+#include "Event.h" >+#include "EventLoopInput.h" >+#include "Frame.h" >+#include "FunctorInputCursor.h" >+#include "InspectorController.h" >+#include "InspectorPageAgent.h" >+#include "InspectorWebTypeBuilders.h" >+#include "InstrumentingAgents.h" >+#include "Logging.h" >+#include "Page.h" >+#include "ReplayController.h" >+#include "ReplaySession.h" >+#include "ReplaySessionSegment.h" >+#include "SerializationMethods.h" >+#include "WebReplayInputs.h" // For EncodingTraits<InputQueue>. >+#include <inspector/InspectorValues.h> >+#include <wtf/text/CString.h> >+#include <wtf/text/WTFString.h> >+ >+using namespace Inspector; >+ >+namespace WebCore { >+ >+static PassRefPtr<TypeBuilder::Replay::ReplayPosition> buildInspectorObjectForPosition(const ReplayPosition& position) >+{ >+ RefPtr<TypeBuilder::Replay::ReplayPosition> positionObject = TypeBuilder::Replay::ReplayPosition::create() >+ .setSegmentOffset(position.segmentOffset) >+ .setInputOffset(position.inputOffset); >+ >+ return positionObject.release(); >+} >+ >+static PassRefPtr<TypeBuilder::Replay::ReplayInput> buildInspectorObjectForInput(const NondeterministicInputBase& input, size_t offset) >+{ >+ EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(input); >+ RefPtr<TypeBuilder::Replay::ReplayInput> inputObject = TypeBuilder::Replay::ReplayInput::create() >+ .setType(input.type()) >+ .setOffset(offset) >+ .setData(encodedInput.asObject()); >+ >+ if (input.queue() == InputQueue::EventLoopInput) >+ inputObject->setTimestamp(static_cast<const EventLoopInputBase&>(input).timestamp()); >+ >+ return inputObject.release(); >+} >+ >+static PassRefPtr<TypeBuilder::Replay::ReplaySession> buildInspectorObjectForSession(PassRefPtr<ReplaySession> prpSession) >+{ >+ RefPtr<ReplaySession> session = prpSession; >+ RefPtr<TypeBuilder::Array<SegmentIdentifier>> segments = TypeBuilder::Array<SegmentIdentifier>::create(); >+ >+ for (auto it = session->begin(); it != session->end(); ++it) >+ segments->addItem((*it)->identifier()); >+ >+ RefPtr<TypeBuilder::Replay::ReplaySession> sessionObject = TypeBuilder::Replay::ReplaySession::create() >+ .setId(session->identifier()) >+ .setTimestamp(session->timestamp()) >+ .setSegments(segments.release()); >+ >+ return sessionObject; >+} >+ >+class SerializeInputToJSONFunctor { >+public: >+ typedef PassRefPtr<TypeBuilder::Array<TypeBuilder::Replay::ReplayInput>> ReturnType; >+ >+ SerializeInputToJSONFunctor() >+ : m_inputs(TypeBuilder::Array<TypeBuilder::Replay::ReplayInput>::create()) { } >+ ~SerializeInputToJSONFunctor() { } >+ >+ void operator()(size_t index, const NondeterministicInputBase* input) >+ { >+ LOG(WebReplay, "%-25s Writing %5zu: %s\n", "[SerializeInput]", index, input->type().string().ascii().data()); >+ >+ if (RefPtr<TypeBuilder::Replay::ReplayInput> serializedInput = buildInspectorObjectForInput(*input, index)) >+ m_inputs->addItem(serializedInput.release()); >+ } >+ >+ ReturnType returnValue() { return m_inputs.release(); } >+private: >+ RefPtr<TypeBuilder::Array<TypeBuilder::Replay::ReplayInput> > m_inputs; >+}; >+ >+static PassRefPtr<TypeBuilder::Replay::SessionSegment> buildInspectorObjectForSegment(PassRefPtr<ReplaySessionSegment> prpSegment) >+{ >+ RefPtr<ReplaySessionSegment> segment = prpSegment; >+ RefPtr<TypeBuilder::Array<TypeBuilder::Replay::ReplayInputQueue>> queuesObject = TypeBuilder::Array<TypeBuilder::Replay::ReplayInputQueue>::create(); >+ >+ for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++) { >+ SerializeInputToJSONFunctor collector; >+ InputQueue queue = static_cast<InputQueue>(i); >+ RefPtr<TypeBuilder::Array<TypeBuilder::Replay::ReplayInput>> queueInputs = segment->createFunctorCursor()->forEachInputInQueue(queue, collector); >+ >+ RefPtr<TypeBuilder::Replay::ReplayInputQueue> queueObject = TypeBuilder::Replay::ReplayInputQueue::create() >+ .setType(EncodingTraits<InputQueue>::encodeValue(queue).convertTo<String>()) >+ .setInputs(queueInputs); >+ >+ queuesObject->addItem(queueObject.release()); >+ } >+ >+ RefPtr<TypeBuilder::Replay::SessionSegment> segmentObject = TypeBuilder::Replay::SessionSegment::create() >+ .setId(segment->identifier()) >+ .setTimestamp(segment->timestamp()) >+ .setQueues(queuesObject.release()); >+ >+ return segmentObject.release(); >+} >+ >+InspectorReplayAgent::InspectorReplayAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent) >+ : InspectorAgentBase(ASCIILiteral("Replay"), instrumentingAgents) >+ , m_frontendDispatcher(nullptr) >+ , m_backendDispatcher(nullptr) >+ , m_pageAgent(pageAgent) >+ , m_page(nullptr) >+ , m_sessionsMap(SessionsMap()) >+ , m_segmentsMap(SegmentsMap()) >+{ >+} >+ >+InspectorReplayAgent::~InspectorReplayAgent() >+{ >+ ASSERT(!m_sessionsMap.size()); >+ ASSERT(!m_segmentsMap.size()); >+} >+ >+SessionState InspectorReplayAgent::sessionState() const >+{ >+ ASSERT(m_page); >+ return m_page->replayController().sessionState(); >+} >+ >+void InspectorReplayAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) >+{ >+ m_frontendDispatcher = std::make_unique<InspectorReplayFrontendDispatcher>(frontendChannel); >+ m_backendDispatcher = InspectorReplayBackendDispatcher::create(backendDispatcher, this); >+ >+ m_instrumentingAgents->setInspectorReplayAgent(this); >+ m_page = m_pageAgent->page(); >+ ASSERT(sessionState() == SessionState::Inactive); >+ >+ // Keep track of the (default) session currently loaded by ReplayController, >+ // and any segments within the session. >+ RefPtr<ReplaySession> session = m_page->replayController().loadedSession(); >+ m_sessionsMap.add(session->identifier(), session); >+ >+ for (auto it = session->begin(); it != session->end(); ++it) >+ m_segmentsMap.add((*it)->identifier(), *it); >+} >+ >+void InspectorReplayAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) >+{ >+ m_frontendDispatcher = nullptr; >+ m_backendDispatcher.clear(); >+ >+ m_instrumentingAgents->setInspectorReplayAgent(nullptr); >+ m_page = nullptr; >+ >+ // Drop references to all sessions and segments. >+ m_sessionsMap.clear(); >+ m_segmentsMap.clear(); >+} >+ >+void InspectorReplayAgent::frameNavigated(DocumentLoader* loader) >+{ >+ if (sessionState() != SessionState::Inactive) >+ m_page->replayController().frameNavigated(loader); >+} >+ >+void InspectorReplayAgent::frameDetached(Frame* frame) >+{ >+ if (sessionState() != SessionState::Inactive) >+ m_page->replayController().frameDetached(frame); >+} >+ >+void InspectorReplayAgent::sessionCreated(PassRefPtr<ReplaySession> prpSession) >+{ >+ RefPtr<ReplaySession> session = prpSession; >+ >+ SessionsMap::AddResult result = m_sessionsMap.add(session->identifier(), session); >+ // Can't have two sessions with same identifier. >+ ASSERT_UNUSED(result, result.isNewEntry); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->sessionCreated(session->identifier()); >+} >+ >+void InspectorReplayAgent::sessionModified(PassRefPtr<ReplaySession> session) >+{ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->sessionModified(session->identifier()); >+} >+ >+void InspectorReplayAgent::sessionLoaded(PassRefPtr<ReplaySession> prpSession) >+{ >+ RefPtr<ReplaySession> session = prpSession; >+ >+ // In case we didn't know about the loaded session, add here. >+ m_sessionsMap.add(session->identifier(), session); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->sessionLoaded(session->identifier()); >+} >+ >+void InspectorReplayAgent::segmentCreated(PassRefPtr<ReplaySessionSegment> prpSegment) >+{ >+ RefPtr<ReplaySessionSegment> segment = prpSegment; >+ >+ SegmentsMap::AddResult result = m_segmentsMap.add(segment->identifier(), segment); >+ // Can't have two segments with the same identifier. >+ ASSERT_UNUSED(result, result.isNewEntry); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->segmentCreated(segment->identifier()); >+} >+ >+void InspectorReplayAgent::segmentCompleted(PassRefPtr<ReplaySessionSegment> segment) >+{ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->segmentCompleted(segment->identifier()); >+} >+ >+void InspectorReplayAgent::segmentLoaded(PassRefPtr<ReplaySessionSegment> prpSegment) >+{ >+ RefPtr<ReplaySessionSegment> segment = prpSegment; >+ >+ // In case we didn't know about the loaded segment, add here. >+ m_segmentsMap.add(segment->identifier(), segment); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->segmentLoaded(segment->identifier()); >+} >+ >+void InspectorReplayAgent::segmentUnloaded() >+{ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->segmentUnloaded(); >+} >+ >+void InspectorReplayAgent::captureStarted() >+{ >+ LOG(WebReplay, "-----CAPTURE START-----"); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->captureStarted(); >+} >+ >+void InspectorReplayAgent::captureStopped() >+{ >+ LOG(WebReplay, "-----CAPTURE STOP-----"); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->captureStopped(); >+} >+ >+void InspectorReplayAgent::playbackStarted() >+{ >+ LOG(WebReplay, "-----REPLAY START-----"); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->playbackStarted(); >+} >+ >+void InspectorReplayAgent::playbackPaused(const ReplayPosition& position) >+{ >+ LOG(WebReplay, "-----REPLAY PAUSED-----"); >+ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->playbackPaused(buildInspectorObjectForPosition(position)); >+} >+ >+void InspectorReplayAgent::playbackHitPosition(const ReplayPosition& position) >+{ >+ if (m_frontendDispatcher) >+ m_frontendDispatcher->playbackHitPosition(buildInspectorObjectForPosition(position), monotonicallyIncreasingTime()); >+} >+ >+void InspectorReplayAgent::startCapturing(ErrorString* errorString) >+{ >+ if (sessionState() != SessionState::Inactive) { >+ *errorString = "Can't start capturing if the session is already capturing or replaying."; >+ return; >+ } >+ >+ m_page->replayController().startCapturing(); >+} >+ >+void InspectorReplayAgent::stopCapturing(ErrorString* errorString) >+{ >+ if (sessionState() != SessionState::Capturing) { >+ *errorString = "Can't stop capturing if capture is not in progress."; >+ return; >+ } >+ >+ m_page->replayController().stopCapturing(); >+} >+ >+void InspectorReplayAgent::replayToPosition(ErrorString* errorString, const RefPtr<InspectorObject>& positionObject, bool fastReplay) >+{ >+ ReplayPosition position; >+ if (!positionObject->getNumber(ASCIILiteral("segmentOffset"), &position.segmentOffset)) { >+ *errorString = "Couldn't decode ReplayPosition segment offset provided to ReplayAgent.replayToPosition."; >+ return; >+ } >+ >+ if (!positionObject->getNumber(ASCIILiteral("inputOffset"), &position.inputOffset)) { >+ *errorString = "Couldn't decode ReplayPosition input offset provided to ReplayAgent.replayToPosition."; >+ return; >+ } >+ >+ if (sessionState() != SessionState::Inactive) { >+ *errorString = "Can't start replay while capture or playback is in progress."; >+ return; >+ } >+ >+ m_page->replayController().replayToPosition(position, (fastReplay) ? DispatchSpeed::FastForward : DispatchSpeed::Realtime); >+} >+ >+void InspectorReplayAgent::replayToCompletion(ErrorString* errorString, bool fastReplay) >+{ >+ if (sessionState() != SessionState::Inactive) { >+ *errorString = "Can't start replay while capture or playback is in progress."; >+ return; >+ } >+ >+ m_page->replayController().replayToCompletion((fastReplay) ? DispatchSpeed::FastForward : DispatchSpeed::Realtime); >+} >+ >+void InspectorReplayAgent::pausePlayback(ErrorString* errorString) >+{ >+ if (sessionState() != SessionState::Replaying) { >+ *errorString = "Can't pause playback if playback is not in progress."; >+ return; >+ } >+ >+ m_page->replayController().pausePlayback(); >+} >+ >+void InspectorReplayAgent::cancelPlayback(ErrorString* errorString) >+{ >+ if (sessionState() == SessionState::Capturing) { >+ *errorString = "Can't cancel playback if capture is in progress."; >+ return; >+ } >+ >+ m_page->replayController().cancelPlayback(); >+} >+ >+void InspectorReplayAgent::switchSession(ErrorString* errorString, SessionIdentifier identifier) >+{ >+ ASSERT(identifier > 0); >+ >+ if (sessionState() != SessionState::Inactive) { >+ *errorString = "Can't switch sessions unless the session is neither capturing or replaying."; >+ return; >+ } >+ >+ RefPtr<ReplaySession> session = findSession(errorString, identifier); >+ if (!session) >+ return; >+ >+ m_page->replayController().switchSession(session); >+} >+ >+void InspectorReplayAgent::insertSessionSegment(ErrorString* errorString, SessionIdentifier sessionIdentifier, SegmentIdentifier segmentIdentifier, int segmentIndex) >+{ >+ ASSERT(sessionIdentifier > 0); >+ ASSERT(segmentIdentifier > 0); >+ ASSERT(segmentIndex >= 0); >+ >+ RefPtr<ReplaySession> session = findSession(errorString, sessionIdentifier); >+ RefPtr<ReplaySessionSegment> segment = findSegment(errorString, segmentIdentifier); >+ >+ if (!session || !segment) >+ return; >+ >+ if (static_cast<size_t>(segmentIndex) > session->size()) { >+ *errorString = "Invalid segment index."; >+ return; >+ } >+ >+ if (session == m_page->replayController().loadedSession() && sessionState() != SessionState::Inactive) { >+ *errorString = "Can't modify a loaded session unless the session is inactive."; >+ return; >+ } >+ >+ session->insertSegment(segmentIndex, segment); >+ sessionModified(session); >+} >+ >+void InspectorReplayAgent::removeSessionSegment(ErrorString* errorString, SessionIdentifier identifier, int segmentIndex) >+{ >+ ASSERT(identifier > 0); >+ ASSERT(segmentIndex >= 0); >+ >+ RefPtr<ReplaySession> session = findSession(errorString, identifier); >+ >+ if (!session) >+ return; >+ >+ if (static_cast<size_t>(segmentIndex) >= session->size()) { >+ *errorString = "Invalid segment index."; >+ return; >+ } >+ >+ if (session == m_page->replayController().loadedSession() && sessionState() != SessionState::Inactive) { >+ *errorString = "Can't modify a loaded session unless the session is inactive."; >+ return; >+ } >+ >+ session->removeSegment(segmentIndex); >+ sessionModified(session); >+} >+ >+PassRefPtr<ReplaySession> InspectorReplayAgent::findSession(ErrorString* errorString, SessionIdentifier identifier) >+{ >+ ASSERT(identifier > 0); >+ >+ SessionsMap::iterator it = m_sessionsMap.find(identifier); >+ if (it == m_sessionsMap.end()) { >+ *errorString = "Couldn't find session with specified identifier"; >+ return nullptr; >+ } >+ >+ return it->value; >+} >+ >+PassRefPtr<ReplaySessionSegment> InspectorReplayAgent::findSegment(ErrorString* errorString, SegmentIdentifier identifier) >+{ >+ ASSERT(identifier > 0); >+ >+ SegmentsMap::iterator it = m_segmentsMap.find(identifier); >+ if (it == m_segmentsMap.end()) { >+ *errorString = "Couldn't find segment with specified identifier"; >+ return nullptr; >+ } >+ >+ return it->value; >+} >+ >+void InspectorReplayAgent::getAvailableSessions(ErrorString*, RefPtr<Inspector::TypeBuilder::Array<SessionIdentifier>>& sessionsList) >+{ >+ sessionsList = TypeBuilder::Array<SessionIdentifier>::create(); >+ for (auto it = m_sessionsMap.begin(); it != m_sessionsMap.end(); ++it) >+ sessionsList->addItem(it->key); >+} >+ >+void InspectorReplayAgent::getSerializedSession(ErrorString* errorString, SessionIdentifier identifier, RefPtr<Inspector::TypeBuilder::Replay::ReplaySession>& serializedObject) >+{ >+ RefPtr<ReplaySession> session = findSession(errorString, identifier); >+ if (!session) { >+ *errorString = "Couldn't find the specified session."; >+ return; >+ } >+ >+ serializedObject = buildInspectorObjectForSession(session); >+} >+ >+void InspectorReplayAgent::getSerializedSegment(ErrorString* errorString, SegmentIdentifier identifier, RefPtr<Inspector::TypeBuilder::Replay::SessionSegment>& serializedObject) >+{ >+ RefPtr<ReplaySessionSegment> segment = findSegment(errorString, identifier); >+ if (!segment) { >+ *errorString = "Couldn't find the specified segment."; >+ return; >+ } >+ >+ serializedObject = buildInspectorObjectForSegment(segment); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(INSPECTOR) && ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/inspector/InspectorReplayAgent.h b/Source/WebCore/inspector/InspectorReplayAgent.h >new file mode 100644 >index 0000000000000000000000000000000000000000..d9b0bc45b712165036439f8d1257299aa254140e >--- /dev/null >+++ b/Source/WebCore/inspector/InspectorReplayAgent.h >@@ -0,0 +1,129 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef InspectorReplayAgent_h >+#define InspectorReplayAgent_h >+ >+#if ENABLE(INSPECTOR) && ENABLE(WEB_REPLAY) >+ >+#include "InspectorWebAgentBase.h" >+#include "InspectorWebBackendDispatchers.h" >+#include "InspectorWebFrontendDispatchers.h" >+#include <wtf/Forward.h> >+#include <wtf/HashMap.h> >+#include <wtf/Noncopyable.h> >+ >+namespace WebCore { >+ >+class DocumentLoader; >+class Frame; >+class InspectorPageAgent; >+class InstrumentingAgents; >+class Page; >+class ReplaySession; >+class ReplaySessionSegment; >+ >+enum class SessionState; >+ >+struct ReplayPosition; >+ >+typedef String ErrorString; >+typedef int SessionIdentifier; >+typedef int SegmentIdentifier; >+ >+class InspectorReplayAgent final >+ : public InspectorAgentBase >+ , public Inspector::InspectorReplayBackendDispatcherHandler { >+ WTF_MAKE_NONCOPYABLE(InspectorReplayAgent); >+public: >+ InspectorReplayAgent(InstrumentingAgents*, InspectorPageAgent*); >+ ~InspectorReplayAgent(); >+ >+ virtual void didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, Inspector::InspectorBackendDispatcher*) override; >+ virtual void willDestroyFrontendAndBackend(Inspector::InspectorDisconnectReason) override; >+ >+ // Callbacks from InspectorInstrumentation. >+ void frameNavigated(DocumentLoader*); >+ void frameDetached(Frame*); >+ >+ // Notifications from ReplayController. >+ void sessionCreated(PassRefPtr<ReplaySession>); >+ // This is called internally (when adding/removing) and by ReplayController during capture. >+ void sessionModified(PassRefPtr<ReplaySession>); >+ void sessionLoaded(PassRefPtr<ReplaySession>); >+ >+ void segmentCreated(PassRefPtr<ReplaySessionSegment>); >+ void segmentCompleted(PassRefPtr<ReplaySessionSegment>); >+ void segmentLoaded(PassRefPtr<ReplaySessionSegment>); >+ void segmentUnloaded(); >+ >+ void captureStarted(); >+ void captureStopped(); >+ >+ void playbackStarted(); >+ void playbackPaused(const ReplayPosition&); >+ void playbackHitPosition(const ReplayPosition&); >+ >+ // Calls from the Inspector frontend. >+ void startCapturing(ErrorString*) override; >+ void stopCapturing(ErrorString*) override; >+ >+ void replayToPosition(ErrorString*, const RefPtr<Inspector::InspectorObject>&, bool shouldFastForward) override; >+ void replayToCompletion(ErrorString*, bool shouldFastForward) override; >+ void pausePlayback(ErrorString*) override; >+ void cancelPlayback(ErrorString*) override; >+ >+ void switchSession(ErrorString*, SessionIdentifier) override; >+ void insertSessionSegment(ErrorString*, SessionIdentifier, SegmentIdentifier, int segmentIndex) override; >+ void removeSessionSegment(ErrorString*, SessionIdentifier, int segmentIndex) override; >+ >+ void getAvailableSessions(ErrorString*, RefPtr<Inspector::TypeBuilder::Array<SessionIdentifier>>&) override; >+ void getSerializedSession(ErrorString*, SessionIdentifier, RefPtr<Inspector::TypeBuilder::Replay::ReplaySession>&) override; >+ void getSerializedSegment(ErrorString*, SegmentIdentifier, RefPtr<Inspector::TypeBuilder::Replay::SessionSegment>&) override; >+ >+private: >+ PassRefPtr<ReplaySession> findSession(ErrorString*, SessionIdentifier); >+ PassRefPtr<ReplaySessionSegment> findSegment(ErrorString*, SegmentIdentifier); >+ SessionState sessionState() const; >+ >+ std::unique_ptr<Inspector::InspectorReplayFrontendDispatcher> m_frontendDispatcher; >+ RefPtr<Inspector::InspectorReplayBackendDispatcher> m_backendDispatcher; >+ InspectorPageAgent* m_pageAgent; >+ Page* m_page; >+ >+ typedef HashMap<int, RefPtr<ReplaySession>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> SessionsMap; >+ SessionsMap m_sessionsMap; >+ >+ typedef HashMap<int, RefPtr<ReplaySessionSegment>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> SegmentsMap; >+ SegmentsMap m_segmentsMap; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(INSPECTOR) && ENABLE(WEB_REPLAY) >+ >+#endif // InspectorReplayAgent_h >diff --git a/Source/WebCore/inspector/InstrumentingAgents.cpp b/Source/WebCore/inspector/InstrumentingAgents.cpp >index b22c7b5712f562370972b827776a342b21752b09..fd886ad8bda71bf0560633b087a242e34e5b6ec3 100644 >--- a/Source/WebCore/inspector/InstrumentingAgents.cpp >+++ b/Source/WebCore/inspector/InstrumentingAgents.cpp >@@ -1,5 +1,6 @@ > /* > * Copyright (C) 2011 Google Inc. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions are >@@ -56,6 +57,9 @@ InstrumentingAgents::InstrumentingAgents(InspectorEnvironment& environment) > , m_workerRuntimeAgent(nullptr) > , m_inspectorTimelineAgent(nullptr) > , m_inspectorDOMStorageAgent(nullptr) >+#if ENABLE(WEB_REPLAY) >+ , m_inspectorReplayAgent(nullptr) >+#endif > #if ENABLE(SQL_DATABASE) > , m_inspectorDatabaseAgent(nullptr) > #endif >@@ -82,6 +86,9 @@ void InstrumentingAgents::reset() > m_workerRuntimeAgent = nullptr; > m_inspectorTimelineAgent = nullptr; > m_inspectorDOMStorageAgent = nullptr; >+#if ENABLE(WEB_REPLAY) >+ m_inspectorReplayAgent = nullptr; >+#endif > #if ENABLE(SQL_DATABASE) > m_inspectorDatabaseAgent = nullptr; > #endif >diff --git a/Source/WebCore/inspector/InstrumentingAgents.h b/Source/WebCore/inspector/InstrumentingAgents.h >index 37fab15049c9de6284adba2577a3316e55af0421..4f8b05819380eae325af68f80435a62b9ec0867b 100644 >--- a/Source/WebCore/inspector/InstrumentingAgents.h >+++ b/Source/WebCore/inspector/InstrumentingAgents.h >@@ -1,5 +1,6 @@ > /* > * Copyright (C) 2011 Google Inc. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions are >@@ -56,6 +57,7 @@ class InspectorLayerTreeAgent; > class InspectorPageAgent; > class InspectorProfilerAgent; > class InspectorResourceAgent; >+class InspectorReplayAgent; > class InspectorTimelineAgent; > class InspectorWorkerAgent; > class Page; >@@ -108,6 +110,11 @@ public: > InspectorDOMStorageAgent* inspectorDOMStorageAgent() const { return m_inspectorDOMStorageAgent; } > void setInspectorDOMStorageAgent(InspectorDOMStorageAgent* agent) { m_inspectorDOMStorageAgent = agent; } > >+#if ENABLE(WEB_REPLAY) >+ InspectorReplayAgent* inspectorReplayAgent() const { return m_inspectorReplayAgent; } >+ void setInspectorReplayAgent(InspectorReplayAgent* agent) { m_inspectorReplayAgent = agent; } >+#endif >+ > #if ENABLE(SQL_DATABASE) > InspectorDatabaseAgent* inspectorDatabaseAgent() const { return m_inspectorDatabaseAgent; } > void setInspectorDatabaseAgent(InspectorDatabaseAgent* agent) { m_inspectorDatabaseAgent = agent; } >@@ -156,6 +163,9 @@ private: > WorkerRuntimeAgent* m_workerRuntimeAgent; > InspectorTimelineAgent* m_inspectorTimelineAgent; > InspectorDOMStorageAgent* m_inspectorDOMStorageAgent; >+#if ENABLE(WEB_REPLAY) >+ InspectorReplayAgent* m_inspectorReplayAgent; >+#endif > #if ENABLE(SQL_DATABASE) > InspectorDatabaseAgent* m_inspectorDatabaseAgent; > #endif >diff --git a/Source/WebCore/inspector/protocol/Replay.json b/Source/WebCore/inspector/protocol/Replay.json >new file mode 100644 >index 0000000000000000000000000000000000000000..beb017a5489e1b960acb8ab70ed224f08dca8a44 >--- /dev/null >+++ b/Source/WebCore/inspector/protocol/Replay.json >@@ -0,0 +1,243 @@ >+{ >+ "domain": "Replay", >+ "description": "Controls web replay, and manages recording sessions and segments.", >+ "types": [ >+ { >+ "id": "SessionIdentifier", "description": "Unique replay session identifier.", >+ "type": "integer" >+ }, >+ { >+ "id": "SegmentIdentifier", "description": "Unique session segment identifier.", >+ "type": "integer" >+ }, >+ { >+ "id": "ReplayPosition", >+ "type": "object", >+ "properties": [ >+ { "name": "segmentOffset", "type": "integer", "description": "Offset for an event loop input within the specified session segment." }, >+ { "name": "inputOffset", "type": "integer", "description": "Offset for an event loop input within the specified session segment." } >+ ] >+ }, >+ { >+ "id": "ReplayInput", >+ "type": "object", >+ "properties": [ >+ { "name": "type", "type": "string", "description": "Input type." }, >+ { "name": "offset", "type": "integer", "description": "Offset of this input in its respective queue."}, >+ { "name": "timestamp", "type": "number", "optional": true, "description": "The timestamp of this input." }, >+ { "name": "data", "type": "object", "description": "Per-input payload." } >+ ] >+ }, >+ { >+ "id": "ReplayInputQueue", >+ "type": "object", >+ "properties": [ >+ { "name": "type", "type": "string", "description": "Queue type" }, >+ { "name": "inputs", "type": "array", "items": { "$ref": "ReplayInput"}, "description": "Inputs belonging to this queue." } >+ ] >+ }, >+ { >+ "id": "SessionSegment", "description": "A standalone segment of a replay session that corresponds to a single main frame navigation and execution.", >+ "type": "object", >+ "properties": [ >+ { "name": "id", "$ref": "SegmentIdentifier", "description": "Unique session segment identifier." }, >+ { "name": "timestamp", "type": "number", "description": "Start time of the segment, in milliseconds since the epoch." }, >+ { "name": "queues", "type": "array", "items": { "$ref": "ReplayInputQueue"} } >+ ] >+ }, >+ { >+ "id": "ReplaySession", "description": "An ordered collection of replay session segments.", >+ "type": "object", >+ "properties": [ >+ { "name": "id", "$ref": "SessionIdentifier", "description": "Unique replay session identifier." }, >+ { "name": "timestamp", "type": "number", "description": "Creation time of session, in milliseconds since the epoch." }, >+ { "name": "segments", "type": "array", "items": { "$ref": "SegmentIdentifier" }, "description": "An ordered list identifiers for the segments that comprise this replay session." } >+ ] >+ } >+ ], >+ "commands": [ >+ { >+ "name": "startCapturing", >+ "description": "Starts capture of a new replay session." >+ }, >+ { >+ "name": "stopCapturing", >+ "description": "Stops capture of the currently recording replay session." >+ }, >+ { >+ "name": "replayToPosition", >+ "description": "Seek execution to a specific position within the replay session.", >+ "parameters": [ >+ { "name": "position", "$ref": "ReplayPosition" }, >+ { "name": "shouldFastForward", "type": "boolean" } >+ ] >+ }, >+ { >+ "name": "replayToCompletion", >+ "description": "Replay all session segments completely.", >+ "parameters": [ >+ { "name": "shouldFastForward", "type": "boolean" } >+ ] >+ }, >+ { >+ "name": "pausePlayback", >+ "description": "Pauses playback in the current segment. Can be resumed by using a replay command." >+ }, >+ { >+ "name": "cancelPlayback", >+ "description": "Cancels playback of the current segment. Further replaying will start from the beginning of the current segment." >+ }, >+ { >+ "name": "switchSession", >+ "description": "Unloads the current replay session and loads the specified session", >+ "parameters": [ >+ { "name": "sessionIdentifier", "$ref": "SessionIdentifier" } >+ ] >+ }, >+ { >+ "name": "insertSessionSegment", >+ "description": "Splices the specified session segment into the session at the specified index.", >+ "parameters": [ >+ { "name": "sessionIdentifier", "$ref": "SessionIdentifier" }, >+ { "name": "segmentIdentifier", "$ref": "SegmentIdentifier" }, >+ { "name": "segmentIndex", "type": "integer" } >+ ] >+ }, >+ { >+ "name": "removeSessionSegment", >+ "description": "Removes the session segment at the specified position from the session.", >+ "parameters": [ >+ { "name": "sessionIdentifier", "$ref": "SessionIdentifier" }, >+ { "name": "segmentIndex", "type": "integer" } >+ ] >+ }, >+ { >+ "name": "getAvailableSessions", >+ "description": "Returns identifiers of all available sessions.", >+ "returns": [ >+ { "name": "ids", "type": "array", "items": { "$ref": "SessionIdentifier" } } >+ ] >+ }, >+ { >+ "name": "getSerializedSession", >+ "description": "Returns the specified session serialized to a JSON object.", >+ "parameters": [ >+ { "name": "sessionId", "$ref": "SessionIdentifier" } >+ ], >+ "returns": [ >+ { "name": "session", "$ref": "ReplaySession", "description": "The requested serialized replay session.", "optional": true } >+ ] >+ }, >+ { >+ "name": "getSerializedSegment", >+ "description": "Returns the session segment serialized to a JSON object.", >+ "parameters": [ >+ { "name": "id", "$ref": "SegmentIdentifier" } >+ ], >+ "returns": [ >+ { "name": "segment", "$ref": "SessionSegment", "description": "The requested serialized session segment.", "optional": true } >+ ] >+ } >+ ], >+ >+ "events": [ >+ { >+ "name": "captureStarted", >+ "description": "Fired when capture has started." >+ }, >+ { >+ "name": "captureStopped", >+ "description": "Fired when capture has stopped." >+ }, >+ { >+ "name": "playbackHitPosition", >+ "description": "A position was reached during playback of the session.", >+ "parameters": [ >+ { "name": "position", "$ref": "ReplayPosition", "description": "The playback position that was hit." }, >+ { "name": "timestamp", "type": "number", "description": "A timestamp for the event." } >+ ] >+ }, >+ { >+ "name": "playbackStarted", >+ "description": "Fired when session playback has started." >+ }, >+ { >+ "name": "playbackPaused", >+ "description": "Fired when session playback has paused, but not finished.", >+ "parameters": [ >+ { "name": "position", "$ref": "ReplayPosition", "description": "The playback position immediately prior to where playback is paused." } >+ ] >+ }, >+ { >+ "name": "playbackFinished", >+ "description": "Fired when session playback has stopped." >+ }, >+ { >+ "name": "inputSuppressionChanged", >+ "description": "Fired when the replay controller starts or stops suppressing user inputs.", >+ "parameters": [ >+ { "name": "willSuppress", "type": "boolean", "description": "Whether user inputs will be suppressed during playback." } >+ ] >+ }, >+ { >+ "name": "sessionCreated", >+ "description": "Fired when a new replay session is created", >+ "parameters": [ >+ { "name": "id", "$ref": "SessionIdentifier", "description": "Identifier for the created session." } >+ ] >+ }, >+ { >+ "name": "sessionModified", >+ "description": "Fired when a session's segments have changed.", >+ "parameters": [ >+ { "name": "id", "$ref": "SessionIdentifier", "description": "Identifier for the session the segment was added to." } >+ ] >+ }, >+ { >+ "name": "sessionRemoved", >+ "description": "Fired when a replay session is removed and can no longer be loaded.", >+ "parameters": [ >+ { "name": "id", "$ref": "SessionIdentifier", "description": "Identifier for the removed session." } >+ ] >+ }, >+ { >+ "name": "sessionLoaded", >+ "description": "Fired when a replay session is loaded.", >+ "parameters": [ >+ { "name": "id", "$ref": "SessionIdentifier", "description": "Identifier for the loaded session." } >+ ] >+ }, >+ { >+ "name": "segmentCreated", >+ "description": "Fired when a new session segment is created.", >+ "parameters": [ >+ { "name": "id", "$ref": "SegmentIdentifier", "description": "Identifier for the created session segment." } >+ ] >+ }, >+ { >+ "name": "segmentRemoved", >+ "description": "Fired when a session segment is removed and can no longer be replayed as part of a session.", >+ "parameters": [ >+ { "name": "id", "$ref": "SegmentIdentifier", "description": "Identifier for the removed session segment." } >+ ] >+ }, >+ { >+ "name": "segmentCompleted", >+ "description": "Fired when a session segment is completed and can no longer have inputs added to it.", >+ "parameters": [ >+ { "name": "id", "$ref": "SegmentIdentifier", "description": "Identifier for the completed session segment." } >+ ] >+ }, >+ { >+ "name": "segmentLoaded", >+ "description": "Fired when a segment is loaded.", >+ "parameters": [ >+ { "name": "segmentId", "$ref": "SegmentIdentifier", "description": "Id for the loaded segment." } >+ ] >+ }, >+ { >+ "name": "segmentUnloaded", >+ "description": "Fired when a segment is unloaded." >+ } >+ ] >+} >diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp >index ca525795f6217f6810addb940f079fcca9243da5..81b68dc1b010b83ae7292aaf3bfb46bc7af1a73d 100644 >--- a/Source/WebCore/page/Page.cpp >+++ b/Source/WebCore/page/Page.cpp >@@ -92,6 +92,10 @@ > #include <wtf/text/Base64.h> > #include <wtf/text/StringHash.h> > >+#if ENABLE(WEB_REPLAY) >+#include "ReplayController.h" >+#endif >+ > namespace WebCore { > > static HashSet<Page*>* allPages; >@@ -136,6 +140,9 @@ Page::Page(PageClients& pageClients) > #if ENABLE(CONTEXT_MENUS) > , m_contextMenuController(std::make_unique<ContextMenuController>(*this, *pageClients.contextMenuClient)) > #endif >+#if ENABLE(WEB_REPLAY) >+ , m_replayController(std::make_unique<ReplayController>(*this)) >+#endif > #if ENABLE(INSPECTOR) > , m_inspectorController(std::make_unique<InspectorController>(*this, pageClients.inspectorClient)) > #endif >diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h >index 96f118d172953459bc6b578146e42b2b03612126..6185ac735cfc8dfc4ee5b7482d6e35147e7e33fe 100644 >--- a/Source/WebCore/page/Page.h >+++ b/Source/WebCore/page/Page.h >@@ -98,6 +98,7 @@ class ProgressTrackerClient; > class Range; > class RenderObject; > class RenderTheme; >+class ReplayController; > class VisibleSelection; > class ScrollableArea; > class ScrollingCoordinator; >@@ -196,6 +197,9 @@ public: > #if ENABLE(CONTEXT_MENUS) > ContextMenuController& contextMenuController() const { return *m_contextMenuController; } > #endif >+#if ENABLE(WEB_REPLAY) >+ ReplayController& replayController() const { return *m_replayController; } >+#endif > #if ENABLE(INSPECTOR) > InspectorController& inspectorController() const { return *m_inspectorController; } > #endif >@@ -439,6 +443,9 @@ private: > #if ENABLE(CONTEXT_MENUS) > const std::unique_ptr<ContextMenuController> m_contextMenuController; > #endif >+#if ENABLE(WEB_REPLAY) >+ const std::unique_ptr<ReplayController> m_replayController; >+#endif > #if ENABLE(INSPECTOR) > const std::unique_ptr<InspectorController> m_inspectorController; > #endif >diff --git a/Source/WebCore/platform/Logging.h b/Source/WebCore/platform/Logging.h >index 4ef22f63267646690ffd2d45b3cf0511589a62a7..906248229f603828dfa829cd53c816e1b62db39f 100644 >--- a/Source/WebCore/platform/Logging.h >+++ b/Source/WebCore/platform/Logging.h >@@ -70,6 +70,7 @@ namespace WebCore { > M(Threading) \ > M(WebAudio) \ > M(WebGL) \ >+ M(WebReplay) \ > > #define DECLARE_LOG_CHANNEL(name) \ > extern WTFLogChannel JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, name); >diff --git a/Source/WebCore/replay/AllReplayInputs.h b/Source/WebCore/replay/AllReplayInputs.h >new file mode 100644 >index 0000000000000000000000000000000000000000..7a951e270d2c68081fb900bb46c13a5420e9ee28 >--- /dev/null >+++ b/Source/WebCore/replay/AllReplayInputs.h >@@ -0,0 +1,49 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// This file is the include equivalent for WEB_REPLAY_INPUT_NAMES_FOR_EACH. >+// Note that there is not an exact correspondence between the two, since >+// Some input types reside in the same file. >+ >+// Make sure that this stays in sync with ReplayInputTypes.h for custom inputs. >+ >+#ifndef AllReplayInputs_h >+#define AllReplayInputs_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "WebReplayInputs.h" >+#include <JavaScriptCore/JSReplayInputs.h> >+ >+#define IMPORT_FROM_JSC_NAMESPACE(name) \ >+using JSC::name; \ >+ >+JS_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_JSC_NAMESPACE) >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // AllReplayInputs_h >diff --git a/Source/WebCore/replay/CapturingInputCursor.cpp b/Source/WebCore/replay/CapturingInputCursor.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..29c62df6190919a4bd4b29d2eca429989ea61a41 >--- /dev/null >+++ b/Source/WebCore/replay/CapturingInputCursor.cpp >@@ -0,0 +1,78 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "CapturingInputCursor.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "Logging.h" >+#include "SegmentedInputStorage.h" >+ >+namespace WebCore { >+ >+CapturingInputCursor::CapturingInputCursor(SegmentedInputStorage* storage) >+ : m_storage(storage) >+{ >+ ASSERT(m_storage); >+ >+ LOG(WebReplay, "%-30sCreated capture cursor=%p.\n", "[ReplayController]", (void*)this); >+} >+ >+CapturingInputCursor::~CapturingInputCursor() >+{ >+ LOG(WebReplay, "%-30sDestroyed capture cursor=%p.\n", "[ReplayController]", (void*)this); >+} >+ >+PassRefPtr<CapturingInputCursor> CapturingInputCursor::create(SegmentedInputStorage* storage) >+{ >+ return adoptRef(new CapturingInputCursor(storage)); >+} >+ >+void CapturingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase> input) >+{ >+ ASSERT(input); >+ m_storage->store(std::move(input)); >+} >+ >+NondeterministicInputBase* CapturingInputCursor::loadInput(InputQueue, const AtomicString&) >+{ >+ // Can't load inputs from capturing cursor. >+ ASSERT_NOT_REACHED(); >+ return nullptr; >+} >+ >+NondeterministicInputBase* CapturingInputCursor::uncheckedLoadInput(InputQueue) >+{ >+ // Can't load inputs from capturing cursor. >+ ASSERT_NOT_REACHED(); >+ return nullptr; >+} >+ >+}; // namespace WebCore >+ >+#endif // ENABLE(TIMEALPSE) >diff --git a/Source/WebCore/replay/CapturingInputCursor.h b/Source/WebCore/replay/CapturingInputCursor.h >new file mode 100644 >index 0000000000000000000000000000000000000000..482e9b259b17fce810babdbf0bd637f600a91b67 >--- /dev/null >+++ b/Source/WebCore/replay/CapturingInputCursor.h >@@ -0,0 +1,64 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef CapturingInputCursor_h >+#define CapturingInputCursor_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <replay/InputCursor.h> >+#include <wtf/Noncopyable.h> >+ >+namespace WebCore { >+ >+class EventLoopInputBase; >+class SegmentedInputStorage; >+ >+class CapturingInputCursor final : public InputCursor { >+ WTF_MAKE_NONCOPYABLE(CapturingInputCursor); >+public: >+ static PassRefPtr<CapturingInputCursor> create(SegmentedInputStorage*); >+ virtual ~CapturingInputCursor(); >+ >+ virtual bool isCapturing() const override { return true; } >+ virtual bool isReplaying() const override { return false; } >+ >+ virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override; >+ virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override; >+protected: >+ virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString& type) override; >+private: >+ CapturingInputCursor(SegmentedInputStorage*); >+ >+ SegmentedInputStorage* m_storage; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // CapturingInputCursor_h >diff --git a/Source/WebCore/replay/EventLoopInput.h b/Source/WebCore/replay/EventLoopInput.h >index abb7d442c4b283e86be259f2cd035e6cf9db14eb..b4e3b70a0612e5316584005bccb985afef316a67 100644 >--- a/Source/WebCore/replay/EventLoopInput.h >+++ b/Source/WebCore/replay/EventLoopInput.h >@@ -37,38 +37,22 @@ namespace WebCore { > > class ReplayController; > >-struct ReplayPosition { >-public: >- ReplayPosition() >- : m_index(0) >- , m_time(0.0) { } >- >- explicit ReplayPosition(unsigned index) >- : m_index(index) >- , m_time(monotonicallyIncreasingTime()) { } >- >- unsigned index() const { return m_index; } >- double time() const { return m_time; } >-private: >- unsigned m_index; >- double m_time; >-}; >- > class EventLoopInputBase : public NondeterministicInputBase { > public: > EventLoopInputBase() >- : m_position(ReplayPosition()) { } >+ : m_timestamp(monotonicallyIncreasingTime()) >+ { >+ } > > virtual ~EventLoopInputBase() { } > virtual InputQueue queue() const override final { return InputQueue::EventLoopInput; } > > virtual void dispatch(ReplayController&) = 0; > >- // During capture, the position is set when the following event loop input is captured. >- void setPosition(const ReplayPosition& position) { m_position = position; } >- ReplayPosition position() const { return m_position; } >+ double timestamp() const { return m_timestamp; } >+ void setTimestamp(double timestamp) { m_timestamp = timestamp; } > protected: >- ReplayPosition m_position; >+ double m_timestamp; > }; > > template <typename InputType> >diff --git a/Source/WebCore/replay/EventLoopInputDispatcher.cpp b/Source/WebCore/replay/EventLoopInputDispatcher.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..27bb841cb59a865592d89d5727d69fbee587dd08 >--- /dev/null >+++ b/Source/WebCore/replay/EventLoopInputDispatcher.cpp >@@ -0,0 +1,174 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "EventLoopInputDispatcher.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "Page.h" >+#include "ReplayInputTypes.h" >+#include "ReplayingInputCursor.h" >+#include <wtf/TemporaryChange.h> >+ >+#if !LOG_DISABLED >+#include "Logging.h" >+#include "SerializationMethods.h" >+#include <replay/EncodedValue.h> >+#include <wtf/text/CString.h> >+#endif >+ >+namespace WebCore { >+ >+EventLoopInputDispatcher::EventLoopInputDispatcher(Page* page, ReplayingInputCursor* cursor, EventLoopInputDispatcherClient* client) >+ : m_page(page) >+ , m_client(client) >+ , m_cursor(cursor) >+ , m_timer(this, &EventLoopInputDispatcher::timerFired) >+ , m_runningInput(nullptr) >+ , m_dispatching(false) >+ , m_running(false) >+ , m_speed(DispatchSpeed::FastForward) >+ , m_previousDispatchStartTime(0.0) >+ , m_previousInputTimestamp(0.0) >+{ >+} >+ >+void EventLoopInputDispatcher::run() >+{ >+ ASSERT(!m_running); >+ m_running = true; >+ >+ LOG(WebReplay, "%-20s Starting dispatch of event loop inputs for page: %p\n", "ReplayEvents", (void*)m_page); >+ dispatchInputSoon(); >+} >+ >+void EventLoopInputDispatcher::pause() >+{ >+ ASSERT(!m_dispatching); >+ ASSERT(m_running); >+ m_running = false; >+ >+ LOG(WebReplay, "%-20s Pausing dispatch of event loop inputs for page: %p\n", "ReplayEvents", (void*)m_page); >+ if (m_timer.isActive()) >+ m_timer.stop(); >+} >+ >+void EventLoopInputDispatcher::timerFired(Timer<EventLoopInputDispatcher>*) >+{ >+ dispatchInput(); >+} >+ >+void EventLoopInputDispatcher::dispatchInputSoon() >+{ >+ ASSERT(m_running); >+ >+ // We may already have an input if replay was paused just before dispatching. >+ if (!m_runningInput) >+ m_runningInput = safeCast<EventLoopInputBase*>(m_cursor->uncheckedLoadInput(InputQueue::EventLoopInput)); >+ >+ if (m_timer.isActive()) >+ m_timer.stop(); >+ >+ double waitInterval = 0; >+ >+ if (m_speed == DispatchSpeed::Realtime) { >+ // The goal is to reproduce the dispatch delay between inputs is it was >+ // was observed during the recording. So, we need to compute how much time >+ // to wait such that the elapsed time plus the wait time will equal the >+ // observed delay between the previous and current input. >+ >+ if (!m_previousInputTimestamp) >+ m_previousInputTimestamp = m_runningInput->timestamp(); >+ >+ double targetInterval = m_runningInput->timestamp() - m_previousInputTimestamp; >+ double elapsed = monotonicallyIncreasingTime() - m_previousDispatchStartTime; >+ waitInterval = targetInterval - elapsed; >+ } >+ >+ // A negative wait time means that dispatch took longer on replay than on >+ // capture. In this case, proceed without waiting at all. >+ if (waitInterval < 0) >+ waitInterval = 0; >+ >+ if (waitInterval > 1000.0) { >+ LOG_ERROR("%-20s Tried to wait for over 1000 seconds before dispatching next event loop input; this is probably a bug.", "ReplayEvents"); >+ waitInterval = 0; >+ } >+ >+ LOG(WebReplay, "%-20s (WAIT: %.3f ms)", "ReplayEvents", waitInterval * 1000.0); >+ m_timer.startOneShot(waitInterval); >+} >+ >+void EventLoopInputDispatcher::dispatchInput() >+{ >+ ASSERT(m_runningInput); >+ ASSERT(!m_dispatching); >+ >+ if (m_speed == DispatchSpeed::Realtime) { >+ m_previousDispatchStartTime = monotonicallyIncreasingTime(); >+ m_previousInputTimestamp = m_runningInput->timestamp(); >+ } >+ >+#if !LOG_DISABLED >+ EncodedValue encodedInput = EncodingTraits<NondeterministicInputBase>::encodeValue(*m_runningInput); >+ String jsonString = encodedInput.asObject()->toJSONString(); >+ >+ LOG(WebReplay, "%-20s ----------------------------------------------", "ReplayEvents"); >+ LOG(WebReplay, "%-20s >DISPATCH: %s %s\n", "ReplayEvents", m_runningInput->type().string().utf8().data(), jsonString.utf8().data()); >+#endif >+ >+ m_client->willDispatchInput(*m_runningInput); >+ // Client could stop replay in the previous callback, so check again. >+ if (!m_running) >+ return; >+ >+ { >+ TemporaryChange<bool> change(m_dispatching, true); >+ m_runningInput->dispatch(m_page->replayController()); >+ } >+ >+ EventLoopInputBase* dispatchedInput = m_runningInput; >+ m_runningInput = nullptr; >+ >+ // Notify clients that the event was dispatched. >+ m_client->didDispatchInput(*dispatchedInput); >+ if (dispatchedInput->type() == inputTypes().EndSegmentSentinel) { >+ m_client->didDispatchFinalInput(); >+ return; >+ } >+ >+ // Clients could stop replay during event dispatch, or from any callback above. >+ if (!m_running) >+ return; >+ >+ dispatchInputSoon(); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/EventLoopInputDispatcher.h b/Source/WebCore/replay/EventLoopInputDispatcher.h >new file mode 100644 >index 0000000000000000000000000000000000000000..5bda67296c80912ef43597c8f99d0871cf2bc0a6 >--- /dev/null >+++ b/Source/WebCore/replay/EventLoopInputDispatcher.h >@@ -0,0 +1,95 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef EventLoopInputDispatcher_h >+#define EventLoopInputDispatcher_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "EventLoopInput.h" >+#include "Timer.h" >+#include <wtf/Noncopyable.h> >+#include <wtf/Vector.h> >+#include <wtf/text/WTFString.h> >+ >+namespace WebCore { >+ >+class Page; >+class ReplayingInputCursor; >+ >+enum class DispatchSpeed { >+ Realtime, >+ FastForward, >+}; >+ >+class EventLoopInputDispatcherClient { >+public: >+ EventLoopInputDispatcherClient() { } >+ virtual ~EventLoopInputDispatcherClient() { } >+ >+ virtual void willDispatchInput(const EventLoopInputBase&) =0; >+ virtual void didDispatchInput(const EventLoopInputBase&) =0; >+ virtual void didDispatchFinalInput() =0; >+}; >+ >+class EventLoopInputDispatcher final { >+ WTF_MAKE_NONCOPYABLE(EventLoopInputDispatcher); >+public: >+ EventLoopInputDispatcher(Page*, ReplayingInputCursor*, EventLoopInputDispatcherClient*); >+ >+ void run(); >+ void pause(); >+ >+ void setDispatchSpeed(DispatchSpeed speed) { m_speed = speed; } >+ DispatchSpeed dispatchSpeed() const { return m_speed; } >+private: >+ void dispatchInputSoon(); >+ void dispatchInput(); >+ void timerFired(Timer<EventLoopInputDispatcher>*); >+ >+ Page* m_page; >+ EventLoopInputDispatcherClient* m_client; >+ ReplayingInputCursor* m_cursor; >+ Timer<EventLoopInputDispatcher> m_timer; >+ >+ // This pointer is valid when an event loop input is presently dispatching. >+ EventLoopInputBase* m_runningInput; >+ bool m_dispatching; >+ bool m_running; >+ >+ DispatchSpeed m_speed; >+ // The time at which the last input dispatch() method was called. >+ double m_previousDispatchStartTime; >+ // The timestamp specified by the last dispatched input. >+ double m_previousInputTimestamp; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // EventLoopInputDispatcher_h >diff --git a/Source/WebCore/replay/FunctorInputCursor.h b/Source/WebCore/replay/FunctorInputCursor.h >new file mode 100644 >index 0000000000000000000000000000000000000000..aa5c484bca754ab0acafbab524eeeb76cb12db37 >--- /dev/null >+++ b/Source/WebCore/replay/FunctorInputCursor.h >@@ -0,0 +1,98 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef FunctorInputCursor_h >+#define FunctorInputCursor_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "SegmentedInputStorage.h" >+#include <replay/InputCursor.h> >+#include <replay/NondeterministicInput.h> >+#include <wtf/Assertions.h> >+#include <wtf/Noncopyable.h> >+#include <wtf/Vector.h> >+ >+namespace WebCore { >+ >+class FunctorInputCursor : public InputCursor { >+ WTF_MAKE_NONCOPYABLE(FunctorInputCursor); >+public: >+ FunctorInputCursor(SegmentedInputStorage*); >+ virtual ~FunctorInputCursor() { } >+ >+ // InputCursor >+ virtual bool isCapturing() const override final { return false; } >+ virtual bool isReplaying() const override final { return false; } >+ >+ virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override final; >+ virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override final; >+ >+ template<typename Functor> >+ typename Functor::ReturnType forEachInputInQueue(InputQueue, Functor&); >+protected: >+ virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString&) override final; >+private: >+ SegmentedInputStorage* m_storage; >+}; >+ >+template<typename Functor> inline >+typename Functor::ReturnType FunctorInputCursor::forEachInputInQueue(InputQueue queue, Functor& functor) >+{ >+ for (size_t i = 0; i < m_storage->m_queues[static_cast<size_t>(queue)]->size(); i++) >+ functor(i, m_storage->m_queues[static_cast<size_t>(queue)]->at(i).get()); >+ >+ return functor.returnValue(); >+} >+ >+inline FunctorInputCursor::FunctorInputCursor(SegmentedInputStorage* storage) >+ : m_storage(storage) >+{ >+} >+ >+inline void FunctorInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase>) >+{ >+ ASSERT_NOT_REACHED(); >+} >+ >+inline NondeterministicInputBase* FunctorInputCursor::loadInput(InputQueue, const AtomicString&) >+{ >+ ASSERT_NOT_REACHED(); >+ return nullptr; >+} >+ >+inline NondeterministicInputBase* FunctorInputCursor::uncheckedLoadInput(InputQueue) >+{ >+ ASSERT_NOT_REACHED(); >+ return nullptr; >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // FunctorInputCursor_h >diff --git a/Source/WebCore/replay/ReplayController.cpp b/Source/WebCore/replay/ReplayController.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..90141830acf60cef4c4881abd071a8a726047daf >--- /dev/null >+++ b/Source/WebCore/replay/ReplayController.cpp >@@ -0,0 +1,362 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "ReplayController.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "AllReplayInputs.h" >+#include "CapturingInputCursor.h" >+#include "DOMWindow.h" >+#include "DocumentLoader.h" >+#include "Frame.h" >+#include "FrameTree.h" >+#include "InspectorInstrumentation.h" >+#include "Logging.h" >+#include "MainFrame.h" >+#include "Page.h" >+#include "ReplaySession.h" >+#include "ReplaySessionSegment.h" >+#include "ReplayingInputCursor.h" >+#include "ScriptController.h" >+#include "WebReplayInputs.h" >+#include <replay/EmptyInputCursor.h> >+#include <wtf/text/CString.h> >+ >+namespace WebCore { >+ >+ReplayController::ReplayController(Page& page) >+ : m_page(page) >+ , m_loadedSegment(nullptr) >+ , m_loadedSession(ReplaySession::create()) >+ , m_emptyCursor(EmptyInputCursor::create()) >+ , m_activeCursor(nullptr) >+ , m_targetPosition(ReplayPosition(0, 0)) >+ , m_currentPosition(ReplayPosition(0, 0)) >+ , m_segmentState(SegmentState::Unloaded) >+ , m_sessionState(SessionState::Inactive) >+ , m_dispatchSpeed(DispatchSpeed::FastForward) >+{ >+} >+ >+void ReplayController::switchSession(PassRefPtr<ReplaySession> session) >+{ >+ ASSERT(m_segmentState == SegmentState::Unloaded); >+ ASSERT(m_sessionState == SessionState::Inactive); >+ >+ m_loadedSession = session; >+ m_currentPosition = ReplayPosition(0, 0); >+ >+ LOG(WebReplay, "%-20sSwitching sessions from %p to %p.\n", "ReplayController", (void*)m_loadedSession.get(), (void*)session.get()); >+ InspectorInstrumentation::sessionLoaded(&m_page, m_loadedSession); >+} >+ >+void ReplayController::createSegment() >+{ >+ ASSERT(m_sessionState == SessionState::Capturing); >+ ASSERT(m_segmentState == SegmentState::Unloaded); >+ >+ m_segmentState = SegmentState::Appending; >+ >+ // Create a new segment but don't associate it with the current session >+ // until we stop appending to it. This preserves the invariant that >+ // segments associated with a replay session have immutable data. >+ m_loadedSegment = ReplaySessionSegment::create(); >+ >+ LOG(WebReplay, "%-20s Created segment: %p.\n", "ReplayController", (void*)m_loadedSegment.get()); >+ InspectorInstrumentation::segmentCreated(&m_page, m_loadedSegment); >+ >+ m_activeCursor = m_loadedSegment->createCapturingCursor(m_page); >+ m_activeCursor->appendInput<BeginSegmentSentinel>(); >+ >+ std::unique_ptr<InitialNavigation> navigationInput = InitialNavigation::createFromPage(m_page); >+ // Dispatching this input schedules navigation of the main frame, causing a refresh. >+ navigationInput->dispatch(*this); >+ m_activeCursor->storeInput(std::move(navigationInput)); >+} >+ >+void ReplayController::completeSegment() >+{ >+ ASSERT(m_sessionState == SessionState::Capturing); >+ ASSERT(m_segmentState == SegmentState::Appending); >+ >+ m_activeCursor->appendInput<EndSegmentSentinel>(); >+ >+ // Hold on to a reference so unloading the segment doesn't deallocate it. >+ RefPtr<ReplaySessionSegment> segment = m_loadedSegment; >+ m_segmentState = SegmentState::Loaded; >+ unloadSegment(true); >+ >+ LOG(WebReplay, "%-20s Completed segment: %p.\n", "ReplayController", (void*)segment.get()); >+ InspectorInstrumentation::segmentCompleted(&m_page, segment); >+ >+ m_loadedSession->appendSegment(segment); >+ InspectorInstrumentation::sessionModified(&m_page, m_loadedSession); >+} >+ >+void ReplayController::loadSegment(PassRefPtr<ReplaySessionSegment> prpSegment) >+{ >+ RefPtr<ReplaySessionSegment> segment = prpSegment; >+ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Unloaded); >+ ASSERT(segment); >+ ASSERT(!m_loadedSegment); >+ >+ m_loadedSegment = segment; >+ m_segmentState = SegmentState::Loaded; >+ >+ m_activeCursor = m_loadedSegment->createReplayingCursor(m_page, this); >+ dispatcher().setDispatchSpeed(m_dispatchSpeed); >+ >+ LOG(WebReplay, "%-20sLoading segment: %p.\n", "ReplayController", (void*)segment.get()); >+ InspectorInstrumentation::segmentLoaded(&m_page, segment); >+} >+ >+void ReplayController::unloadSegment(bool suppressNotifications) >+{ >+ ASSERT(m_sessionState != SessionState::Inactive); >+ ASSERT(m_segmentState == SegmentState::Loaded); >+ >+ m_segmentState = SegmentState::Unloaded; >+ >+ LOG(WebReplay, "%-20s Clearing input cursors for page: %p\n", "ReplayController", (void*)(&m_page)); >+ >+ m_activeCursor = nullptr; >+ RefPtr<ReplaySessionSegment> unloadedSegment = m_loadedSegment.release(); >+ for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) { >+ frame->script().globalObject(mainThreadNormalWorld())->setInputCursor(m_emptyCursor); >+ frame->document()->setInputCursor(m_emptyCursor); >+ } >+ >+ // When we stop capturing, don't send out segment unloaded events since we >+ // didn't send out the corresponding segmentLoaded event at the start of capture. >+ if (!suppressNotifications) { >+ LOG(WebReplay, "%-20sUnloading segment: %p.\n", "ReplayController", (void*)unloadedSegment.get()); >+ InspectorInstrumentation::segmentUnloaded(&m_page); >+ } >+} >+ >+void ReplayController::startCapturing() >+{ >+ ASSERT(m_sessionState == SessionState::Inactive); >+ ASSERT(m_segmentState == SegmentState::Unloaded); >+ >+ m_sessionState = SessionState::Capturing; >+ >+ LOG(WebReplay, "%-20s Starting capture.\n", "ReplayController"); >+ InspectorInstrumentation::captureStarted(&m_page); >+ >+ m_currentPosition = ReplayPosition(0, 0); >+ createSegment(); >+} >+ >+void ReplayController::stopCapturing() >+{ >+ ASSERT(m_sessionState == SessionState::Capturing); >+ ASSERT(m_segmentState == SegmentState::Appending); >+ >+ completeSegment(); >+ >+ m_sessionState = SessionState::Inactive; >+ >+ LOG(WebReplay, "%-20s Stopping capture.\n", "ReplayController"); >+ InspectorInstrumentation::captureStopped(&m_page); >+} >+ >+void ReplayController::startPlayback() >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Loaded); >+ >+ m_segmentState = SegmentState::Dispatching; >+ >+ LOG(WebReplay, "%-20s Starting playback to position (segment: %d, input: %d).\n", "ReplayController", m_targetPosition.segmentOffset, m_targetPosition.inputOffset); >+ InspectorInstrumentation::playbackStarted(&m_page); >+ >+ dispatcher().run(); >+} >+ >+void ReplayController::pausePlayback() >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Dispatching); >+ >+ m_segmentState = SegmentState::Loaded; >+ >+ dispatcher().pause(); >+ >+ LOG(WebReplay, "%-20s Pausing playback at position (segment: %d, input: %d).\n", "ReplayController", m_currentPosition.segmentOffset, m_currentPosition.inputOffset); >+ InspectorInstrumentation::playbackPaused(&m_page, m_currentPosition); >+} >+ >+void ReplayController::cancelPlayback() >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState != SegmentState::Appending); >+ >+ if (m_segmentState == SegmentState::Unloaded) >+ return; >+ >+ if (m_segmentState == SegmentState::Dispatching) >+ pausePlayback(); >+ >+ ASSERT(m_segmentState == SegmentState::Loaded); >+ unloadSegment(); >+ m_sessionState = SessionState::Inactive; >+} >+ >+void ReplayController::replayToPosition(const ReplayPosition& position, DispatchSpeed speed) >+{ >+ ASSERT(m_sessionState != SessionState::Capturing); >+ ASSERT(m_segmentState == SegmentState::Loaded || m_segmentState == SegmentState::Unloaded); >+ ASSERT(position.segmentOffset < m_loadedSession->size()); >+ >+ m_dispatchSpeed = speed; >+ >+ if (m_sessionState != SessionState::Replaying) >+ m_sessionState = SessionState::Replaying; >+ >+ if (m_segmentState == SegmentState::Unloaded) >+ loadSegment(m_loadedSession->at(position.segmentOffset)); >+ else if (position.segmentOffset != m_currentPosition.segmentOffset || m_currentPosition.inputOffset > position.inputOffset) { >+ // If the desired segment is not loaded or we have gone past the desired input >+ // offset, then unload the current segment and load the appropriate segment. >+ unloadSegment(); >+ loadSegment(m_loadedSession->at(position.segmentOffset)); >+ } >+ >+ ASSERT(m_currentPosition.segmentOffset == position.segmentOffset); >+ ASSERT(m_loadedSession->at(position.segmentOffset) == m_loadedSegment); >+ >+ m_targetPosition = position; >+ startPlayback(); >+} >+ >+void ReplayController::frameDetached(Frame* frame) >+{ >+ ASSERT(m_sessionState != SessionState::Inactive); >+ ASSERT(frame); >+ >+ if (!frame->document()) >+ return; >+ >+ // If the frame's cursor isn't capturing or replaying, we should do nothing. >+ // This is the case for the "outbound" frame when starting capture, or when >+ // we clear the input cursor to finish or prematurely unload a segment. >+ if (frame->document()->inputCursor().isCapturing()) { >+ ASSERT(m_segmentState == SegmentState::Appending); >+ completeSegment(); >+ } >+ >+ // During playback, the segments are unloaded and loaded when the final >+ // input has been dispatched. So, nothing needs to be done here. >+} >+ >+void ReplayController::frameNavigated(DocumentLoader* loader) >+{ >+ ASSERT(m_sessionState != SessionState::Inactive); >+ >+ // The initial capturing segment is created prior to main frame navigation. >+ // Otherwise, the prior capturing segment was completed when the frame detached, >+ // and it is now time to create a new segment. >+ if (m_sessionState == SessionState::Capturing && m_segmentState == SegmentState::Unloaded) { >+ m_currentPosition = ReplayPosition(m_currentPosition.segmentOffset + 1, 0); >+ createSegment(); >+ } >+ >+ // During playback, the next segment is loaded when the final input is dispatched, >+ // so nothing needs to be done here. >+ >+ // We store the input cursor in both Document and JSDOMWindow, so that >+ // replay state is accessible from JavaScriptCore and script-free layout code. >+ loader->frame()->document()->setInputCursor(m_activeCursor.get()); >+ loader->frame()->script().globalObject(mainThreadNormalWorld())->setInputCursor(m_activeCursor.get()); >+} >+ >+RefPtr<ReplaySession> ReplayController::loadedSession() const >+{ >+ return m_loadedSession; >+} >+ >+RefPtr<ReplaySessionSegment> ReplayController::loadedSegment() const >+{ >+ return m_loadedSegment; >+} >+ >+InputCursor& ReplayController::activeInputCursor() const >+{ >+ return m_activeCursor ? *m_activeCursor : *m_emptyCursor; >+} >+ >+EventLoopInputDispatcher& ReplayController::dispatcher() const >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Dispatching); >+ ASSERT(m_activeCursor); >+ ASSERT(m_activeCursor->isReplaying()); >+ >+ return static_cast<ReplayingInputCursor&>(*m_activeCursor).dispatcher(); >+} >+ >+void ReplayController::willDispatchInput(const EventLoopInputBase&) >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Dispatching); >+ >+ m_currentPosition.inputOffset++; >+ if (m_currentPosition == m_targetPosition) >+ pausePlayback(); >+} >+ >+void ReplayController::didDispatchInput(const EventLoopInputBase&) >+{ >+ ASSERT(m_sessionState == SessionState::Replaying); >+ ASSERT(m_segmentState == SegmentState::Dispatching); >+ >+ InspectorInstrumentation::playbackHitPosition(&m_page, m_currentPosition); >+} >+ >+void ReplayController::didDispatchFinalInput() >+{ >+ ASSERT(m_segmentState == SegmentState::Dispatching); >+ >+ pause(); >+ unloadSegment(); >+ >+ // No more segments left to replay; stop. >+ if (++m_currentPosition.segmentOffset == m_loadedSession->size()) >+ return; >+ >+ loadSegment(m_loadedSession->at(m_currentPosition.segmentOffset)); >+ startPlayback(); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplayController.h b/Source/WebCore/replay/ReplayController.h >new file mode 100644 >index 0000000000000000000000000000000000000000..4bd7a0a976fe6c7a4ee669596fcde804c20147aa >--- /dev/null >+++ b/Source/WebCore/replay/ReplayController.h >@@ -0,0 +1,177 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef ReplayController_h >+#define ReplayController_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "EventLoopInputDispatcher.h" >+#include <wtf/Noncopyable.h> >+#include <wtf/Vector.h> >+ >+namespace JSC { >+class InputCursor; >+} >+ >+namespace WebCore { >+ >+class ReplaySession; >+class ReplaySessionSegment; >+class DOMWindow; >+class Document; >+class DocumentLoader; >+class Element; >+class Event; >+class EventLoopInputBase; >+class Frame; >+class Node; >+class Page; >+ >+// Each state may transition to the state immediately above or below it. >+// SessionState transitions are only allowed when SegmentState is Unloaded. >+enum class SessionState { >+ Capturing, >+ // Neither capturing or replaying. m_currentPosition is not valid in this state. >+ Inactive, >+ Replaying, >+}; >+ >+// Each state may transition to the state immediately above or below it. >+enum class SegmentState { >+ // Inputs can be appended into an unassociated session segment. >+ // We can stop capturing, which reverts to the Unloaded state. >+ Appending, >+ // No session segment is loaded. >+ // We can start capturing, or load a segment (and then replay it). >+ Unloaded, >+ // A session segment is loaded. >+ // We can unload the segment, or begin playback from m_currentPosition. >+ Loaded, >+ // The controller is actively dispatching event loop inputs. >+ // We can pause or cancel playback, which reverts to the Loaded state. >+ Dispatching, >+}; >+ >+struct ReplayPosition { >+ ReplayPosition(unsigned segmentOffset, unsigned inputOffset) >+ : segmentOffset(segmentOffset) >+ , inputOffset(inputOffset) >+ { >+ } >+ >+ // By convention, this position represents the end of the last segment of the session. >+ ReplayPosition() >+ : segmentOffset(0) >+ , inputOffset(0) >+ { >+ } >+ >+ bool operator<(const ReplayPosition& other) >+ { >+ return segmentOffset <= other.segmentOffset && inputOffset < other.inputOffset; >+ } >+ >+ bool operator==(const ReplayPosition& other) >+ { >+ return segmentOffset == other.segmentOffset && inputOffset == other.inputOffset; >+ } >+ >+ unsigned segmentOffset; >+ unsigned inputOffset; >+}; >+ >+class ReplayController final : public EventLoopInputDispatcherClient { >+ WTF_MAKE_NONCOPYABLE(ReplayController); >+public: >+ ReplayController(Page&); >+ >+ void startCapturing(); >+ void stopCapturing(); >+ >+ // Start or resume playback with default speed and target replay position. >+ void startPlayback(); >+ void pausePlayback(); >+ void cancelPlayback(); >+ >+ void replayToPosition(const ReplayPosition&, DispatchSpeed = DispatchSpeed::FastForward); >+ void replayToCompletion(DispatchSpeed speed = DispatchSpeed::FastForward) >+ { >+ replayToPosition(ReplayPosition(), speed); >+ } >+ >+ void switchSession(PassRefPtr<ReplaySession>); >+ >+ // InspectorReplayAgent notifications. >+ void frameNavigated(DocumentLoader*); >+ void frameDetached(Frame*); >+ >+ Page& page() const { return m_page; } >+ SessionState sessionState() const { return m_sessionState; } >+ RefPtr<ReplaySession> loadedSession() const; >+ RefPtr<ReplaySessionSegment> loadedSegment() const; >+ JSC::InputCursor& activeInputCursor() const; >+ >+private: >+ // EventLoopInputDispatcherClient API >+ virtual void willDispatchInput(const EventLoopInputBase&) override; >+ virtual void didDispatchInput(const EventLoopInputBase&) override; >+ virtual void didDispatchFinalInput() override; >+ >+ void createSegment(); >+ void completeSegment(); >+ >+ void loadSegment(PassRefPtr<ReplaySessionSegment>); >+ void unloadSegment(bool suppressNotifications = false); >+ >+ EventLoopInputDispatcher& dispatcher() const; >+ >+ Page& m_page; >+ >+ RefPtr<ReplaySessionSegment> m_loadedSegment; >+ RefPtr<ReplaySession> m_loadedSession; >+ const RefPtr<JSC::InputCursor> m_emptyCursor; >+ // The active cursor is set to nullptr when invalid. >+ RefPtr<JSC::InputCursor> m_activeCursor; >+ >+ // This position is valid when SessionState == Replaying. >+ ReplayPosition m_targetPosition; >+ // This position is valid when SessionState != Inactive. >+ ReplayPosition m_currentPosition; >+ SegmentState m_segmentState; >+ // This tracks state across multiple segments. When navigating the main frame, >+ // there is a small interval during segment switching when no segment is loaded. >+ SessionState m_sessionState; >+ >+ DispatchSpeed m_dispatchSpeed; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // ReplayController_h >diff --git a/Source/WebCore/replay/ReplayInputCreationMethods.cpp b/Source/WebCore/replay/ReplayInputCreationMethods.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..0ed3e2323c56ec53a98527f8b24bf9098a3e8596 >--- /dev/null >+++ b/Source/WebCore/replay/ReplayInputCreationMethods.cpp >@@ -0,0 +1,54 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "Document.h" >+#include "MainFrame.h" >+#include "Page.h" >+#include "SecurityOrigin.h" >+#include "SerializationMethods.h" >+#include "WebReplayInputs.h" >+ >+namespace WebCore { >+ >+std::unique_ptr<InitialNavigation> InitialNavigation::createFromPage(const Page& page) >+{ >+ const MainFrame& mainFrame = page.mainFrame(); >+ ASSERT(mainFrame.document()); >+ >+ // Make sure that this is in sync with ReplayController::beginCapturing(). >+ RefPtr<SecurityOrigin> originCopy = mainFrame.document()->securityOrigin()->isolatedCopy(); >+ URL url = mainFrame.document()->url(); >+ String referrer = mainFrame.loader().referrer(); >+ return std::make_unique<InitialNavigation>(originCopy, url, referrer); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplayInputDispatchMethods.cpp b/Source/WebCore/replay/ReplayInputDispatchMethods.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..14f76cec2265feffb28f37269b38d021a476a39e >--- /dev/null >+++ b/Source/WebCore/replay/ReplayInputDispatchMethods.cpp >@@ -0,0 +1,57 @@ >+/* >+ * Copyright (C) 2011-2013 University of Washington. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "AllReplayInputs.h" >+#include "MainFrame.h" >+#include "NavigationScheduler.h" >+#include "Page.h" >+#include "ReplayController.h" >+#include "URL.h" >+ >+namespace WebCore { >+ >+// Sentinel inputs. >+void BeginSegmentSentinel::dispatch(ReplayController&) >+{ >+} >+ >+void EndSegmentSentinel::dispatch(ReplayController&) >+{ >+} >+ >+// Navigation inputs. >+void InitialNavigation::dispatch(ReplayController& controller) >+{ >+ controller.page().mainFrame().navigationScheduler().scheduleLocationChange(m_securityOrigin.get(), m_url, m_referrer, true, true); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplayInputTypes.cpp b/Source/WebCore/replay/ReplayInputTypes.cpp >index 8e8919e4ea441b86b12af38418a4406e8aa0ed69..98b14f16351c8666ee7a8519b716a09a6c7a732f 100644 >--- a/Source/WebCore/replay/ReplayInputTypes.cpp >+++ b/Source/WebCore/replay/ReplayInputTypes.cpp >@@ -38,6 +38,7 @@ namespace WebCore { > ReplayInputTypes::ReplayInputTypes() > : dummy(0) > JS_REPLAY_INPUT_NAMES_FOR_EACH(INITIALIZE_INPUT_TYPE) >+WEB_REPLAY_INPUT_NAMES_FOR_EACH(INITIALIZE_INPUT_TYPE) > { > UNUSED_PARAM(dummy); > } >diff --git a/Source/WebCore/replay/ReplayInputTypes.h b/Source/WebCore/replay/ReplayInputTypes.h >index df16ca0f64fc9ad094d952365fc5242232138fb1..b1984232ca922bec500875ca0dbbb340534508c1 100644 >--- a/Source/WebCore/replay/ReplayInputTypes.h >+++ b/Source/WebCore/replay/ReplayInputTypes.h >@@ -31,6 +31,7 @@ > #if ENABLE(WEB_REPLAY) > > #include "ThreadGlobalData.h" >+#include "WebReplayInputs.h" > #include <JavaScriptCore/JSReplayInputs.h> > #include <wtf/text/AtomicString.h> > >@@ -44,6 +45,7 @@ public: > > #define DECLARE_REPLAY_INPUT_TYPES(name) AtomicString name; > JS_REPLAY_INPUT_NAMES_FOR_EACH(DECLARE_REPLAY_INPUT_TYPES) >+ WEB_REPLAY_INPUT_NAMES_FOR_EACH(DECLARE_REPLAY_INPUT_TYPES) > #undef DECLARE_REPLAY_INPUT_TYPES > }; > >diff --git a/Source/WebCore/replay/ReplaySession.cpp b/Source/WebCore/replay/ReplaySession.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..c96afcd826e2bd64b1386c4647effa050375cd13 >--- /dev/null >+++ b/Source/WebCore/replay/ReplaySession.cpp >@@ -0,0 +1,85 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "ReplaySession.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "ReplaySessionSegment.h" >+#include <wtf/CurrentTime.h> >+ >+namespace WebCore { >+ >+static unsigned s_nextIdentifier = 1; >+ >+PassRefPtr<ReplaySession> ReplaySession::create() >+{ >+ return adoptRef(new ReplaySession()); >+} >+ >+ReplaySession::ReplaySession() >+ : m_segments(Vector<RefPtr<ReplaySessionSegment>>()) >+ , m_identifier(s_nextIdentifier++) >+ , m_timestamp(monotonicallyIncreasingTime()) >+{ >+} >+ >+ReplaySession::~ReplaySession() >+{ >+} >+ >+void ReplaySession::appendSegment(PassRefPtr<ReplaySessionSegment> prpSegment) >+{ >+ RefPtr<ReplaySessionSegment> segment = prpSegment; >+ >+ // For now, only support one segment. >+ ASSERT(!m_segments.size()); >+ >+ // Since replay locations are specified with segment IDs, we can only >+ // have one instance of a segment in the session. >+ size_t offset = m_segments.find(segment); >+ ASSERT_UNUSED(offset, offset == notFound); >+ >+ m_segments.append(segment); >+} >+ >+void ReplaySession::insertSegment(size_t position, PassRefPtr<ReplaySessionSegment> segment) >+{ >+ ASSERT(position < m_segments.size()); >+ m_segments.insert(position, segment); >+} >+ >+void ReplaySession::removeSegment(size_t position) >+{ >+ ASSERT(position < m_segments.size()); >+ m_segments.remove(position); >+} >+ >+}; // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplaySession.h b/Source/WebCore/replay/ReplaySession.h >new file mode 100644 >index 0000000000000000000000000000000000000000..dd0f84361c7b3d82cddc8267cd0a86defeb5cf5a >--- /dev/null >+++ b/Source/WebCore/replay/ReplaySession.h >@@ -0,0 +1,74 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef ReplaySession_h >+#define ReplaySession_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <wtf/Noncopyable.h> >+#include <wtf/RefCounted.h> >+#include <wtf/Vector.h> >+ >+namespace WebCore { >+ >+class ReplaySessionSegment; >+ >+typedef Vector<RefPtr<ReplaySessionSegment>>::const_iterator SegmentIterator; >+ >+class ReplaySession : public RefCounted<ReplaySession> { >+ WTF_MAKE_NONCOPYABLE(ReplaySession); >+public: >+ static PassRefPtr<ReplaySession> create(); >+ ~ReplaySession(); >+ >+ unsigned identifier() const { return m_identifier; }; >+ double timestamp() const { return m_timestamp; } >+ >+ size_t size() const { return m_segments.size(); } >+ PassRefPtr<ReplaySessionSegment> at(size_t position) const { return m_segments.at(position); } >+ >+ SegmentIterator begin() const { return m_segments.begin(); }; >+ SegmentIterator end() const { return m_segments.end(); }; >+ >+ void appendSegment(PassRefPtr<ReplaySessionSegment>); >+ void insertSegment(size_t position, PassRefPtr<ReplaySessionSegment>); >+ void removeSegment(size_t position); >+ >+private: >+ ReplaySession(); >+ >+ Vector<RefPtr<ReplaySessionSegment>> m_segments; >+ unsigned m_identifier; >+ double m_timestamp; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // ReplaySession_h >diff --git a/Source/WebCore/replay/ReplaySessionSegment.cpp b/Source/WebCore/replay/ReplaySessionSegment.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..ef21896de83b18a2722aac5a88b48ed33976cd0c >--- /dev/null >+++ b/Source/WebCore/replay/ReplaySessionSegment.cpp >@@ -0,0 +1,79 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "ReplaySessionSegment.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "CapturingInputCursor.h" >+#include "FunctorInputCursor.h" >+#include "ReplayingInputCursor.h" >+#include "SegmentedInputStorage.h" >+#include <wtf/CurrentTime.h> >+ >+namespace WebCore { >+ >+static unsigned s_nextSegmentIdentifier = 1; >+ >+PassRefPtr<ReplaySessionSegment> ReplaySessionSegment::create() >+{ >+ return adoptRef(new ReplaySessionSegment()); >+} >+ >+ReplaySessionSegment::ReplaySessionSegment() >+ : m_storage(std::make_unique<SegmentedInputStorage>()) >+ , m_identifier(s_nextSegmentIdentifier++) >+ , m_canCapture(true) >+ , m_timestamp(monotonicallyIncreasingTime()) >+{ >+} >+ >+ReplaySessionSegment::~ReplaySessionSegment() >+{ >+} >+ >+PassRefPtr<CapturingInputCursor> ReplaySessionSegment::createCapturingCursor(Page&) >+{ >+ ASSERT(m_canCapture); >+ m_canCapture = false; >+ return CapturingInputCursor::create(m_storage.get()); >+} >+ >+PassRefPtr<ReplayingInputCursor> ReplaySessionSegment::createReplayingCursor(Page& page, EventLoopInputDispatcherClient* client) >+{ >+ return ReplayingInputCursor::create(m_storage.get(), &page, client); >+} >+ >+std::unique_ptr<FunctorInputCursor> ReplaySessionSegment::createFunctorCursor() >+{ >+ return std::make_unique<FunctorInputCursor>(m_storage.get()); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplaySessionSegment.h b/Source/WebCore/replay/ReplaySessionSegment.h >new file mode 100644 >index 0000000000000000000000000000000000000000..1b16a390f2479aeef015344d896d8c6605bf407a >--- /dev/null >+++ b/Source/WebCore/replay/ReplaySessionSegment.h >@@ -0,0 +1,69 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef ReplaySessionSegment_h >+#define ReplaySessionSegment_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <replay/NondeterministicInput.h> >+#include <wtf/RefCounted.h> >+ >+namespace WebCore { >+ >+class CapturingInputCursor; >+class EventLoopInputDispatcherClient; >+class FunctorInputCursor; >+class Page; >+class ReplayingInputCursor; >+class SegmentedInputStorage; >+ >+class ReplaySessionSegment : public RefCounted<ReplaySessionSegment> { >+public: >+ static PassRefPtr<ReplaySessionSegment> create(); >+ ~ReplaySessionSegment(); >+ >+ unsigned identifier() const { return m_identifier; } >+ double timestamp() const { return m_timestamp; } >+ >+ PassRefPtr<CapturingInputCursor> createCapturingCursor(Page&); >+ PassRefPtr<ReplayingInputCursor> createReplayingCursor(Page&, EventLoopInputDispatcherClient*); >+ std::unique_ptr<FunctorInputCursor> createFunctorCursor(); >+private: >+ ReplaySessionSegment(); >+ >+ std::unique_ptr<SegmentedInputStorage> m_storage; >+ unsigned m_identifier; >+ bool m_canCapture; >+ double m_timestamp; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // ReplaySessionSegment_h >diff --git a/Source/WebCore/replay/ReplayingInputCursor.cpp b/Source/WebCore/replay/ReplayingInputCursor.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..4aa20764f56989c93a34717104293bbed683b188 >--- /dev/null >+++ b/Source/WebCore/replay/ReplayingInputCursor.cpp >@@ -0,0 +1,90 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "ReplayingInputCursor.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "EventLoopInputDispatcher.h" >+#include "SegmentedInputStorage.h" >+#include "SerializationMethods.h" >+#include "WebReplayInputs.h" >+#include <wtf/text/CString.h> >+ >+namespace WebCore { >+ >+ReplayingInputCursor::ReplayingInputCursor(SegmentedInputStorage* storage, Page* page, EventLoopInputDispatcherClient* client) >+ : m_storage(storage) >+ , m_dispatcher(std::make_unique<EventLoopInputDispatcher>(page, this, client)) >+ , m_positions(Vector<size_t>()) >+{ >+ for (size_t i = 0; i < static_cast<size_t>(InputQueue::Count); i++) >+ m_positions.append(0); >+} >+ >+ReplayingInputCursor::~ReplayingInputCursor() >+{ >+} >+ >+PassRefPtr<ReplayingInputCursor> ReplayingInputCursor::create(SegmentedInputStorage* storage, Page* page, EventLoopInputDispatcherClient* client) >+{ >+ return adoptRef(new ReplayingInputCursor(storage, page, client)); >+} >+ >+void ReplayingInputCursor::storeInput(std::unique_ptr<NondeterministicInputBase>) >+{ >+ // Cannot store inputs from a replaying input cursor. >+ ASSERT_NOT_REACHED(); >+} >+ >+NondeterministicInputBase* ReplayingInputCursor::loadInput(InputQueue queue, const AtomicString& type) >+{ >+ NondeterministicInputBase* input = uncheckedLoadInput(queue); >+ >+ if (input->type() != type) { >+ LOG_ERROR("%-25s ERROR: Expected replay input of type %s, but got type %s\n", "[ReplayingInputCursor]", type.string().ascii().data(), input->type().string().ascii().data()); >+ return nullptr; >+ } >+ >+ return input; >+} >+ >+NondeterministicInputBase* ReplayingInputCursor::uncheckedLoadInput(InputQueue queue) >+{ >+ if (m_positions[static_cast<size_t>(queue)] >= m_storage->queueSize(queue)) { >+ String queueString = EncodingTraits<InputQueue>::encodeValue(queue).convertTo<String>(); >+ LOG_ERROR("%-30s ERROR No more inputs remain for determinism queue %s, but one was requested.", "[ReplayingInputCursor]", queueString.ascii().data()); >+ return nullptr; >+ } >+ >+ return m_storage->load(queue, m_positions[static_cast<size_t>(queue)]++); >+} >+ >+}; // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/ReplayingInputCursor.h b/Source/WebCore/replay/ReplayingInputCursor.h >new file mode 100644 >index 0000000000000000000000000000000000000000..e683b33bc619ee4d3fe15e6df9be233da96c737a >--- /dev/null >+++ b/Source/WebCore/replay/ReplayingInputCursor.h >@@ -0,0 +1,71 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef ReplayingInputCursor_h >+#define ReplayingInputCursor_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <replay/InputCursor.h> >+#include <wtf/Vector.h> >+#include <wtf/text/AtomicString.h> >+ >+namespace WebCore { >+ >+class EventLoopInputDispatcher; >+class EventLoopInputDispatcherClient; >+class Page; >+class SegmentedInputStorage; >+ >+class ReplayingInputCursor final : public InputCursor { >+ WTF_MAKE_NONCOPYABLE(ReplayingInputCursor); >+public: >+ static PassRefPtr<ReplayingInputCursor> create(SegmentedInputStorage*, Page*, EventLoopInputDispatcherClient*); >+ virtual ~ReplayingInputCursor(); >+ >+ virtual bool isCapturing() const override { return false; } >+ virtual bool isReplaying() const override { return true; } >+ >+ EventLoopInputDispatcher& dispatcher() const { return *m_dispatcher; } >+ >+ virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override; >+ virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override; >+protected: >+ virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString& type) override; >+private: >+ ReplayingInputCursor(SegmentedInputStorage*, Page*, EventLoopInputDispatcherClient*); >+ >+ SegmentedInputStorage* m_storage; >+ std::unique_ptr<EventLoopInputDispatcher> m_dispatcher; >+ Vector<size_t> m_positions; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // ReplayingInputCursor_h >diff --git a/Source/WebCore/replay/SegmentedInputStorage.cpp b/Source/WebCore/replay/SegmentedInputStorage.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..f5072f680217903090cdd0725c4326d21714d4ae >--- /dev/null >+++ b/Source/WebCore/replay/SegmentedInputStorage.cpp >@@ -0,0 +1,118 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "SegmentedInputStorage.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#if !LOG_DISABLED >+#include "Logging.h" >+#include "SerializationMethods.h" >+#include <replay/EncodedValue.h> >+#include <wtf/text/CString.h> >+#include <wtf/text/WTFString.h> >+#endif >+ >+namespace WebCore { >+ >+#if !LOG_DISABLED >+static const char* queueTypeToLogPrefix(InputQueue queue, bool isLoad) >+{ >+ if (isLoad) { >+ switch (queue) { >+ case InputQueue::EventLoopInput: return "(DSPTCH-LOAD)"; >+ case InputQueue::LoaderMemoizedData: return "<LDMEMO-LOAD"; >+ case InputQueue::ScriptMemoizedData: return "<---<---<---JSMEMO-LOAD"; >+ case InputQueue::Count: return "ERROR!"; >+ } >+ } else { >+ switch (queue) { >+ case InputQueue::EventLoopInput: return ">DSPTCH-STORE"; >+ case InputQueue::LoaderMemoizedData: return "<LDMEMO-STORE"; >+ case InputQueue::ScriptMemoizedData: return "<---<---<---JSMEMO-STORE"; >+ case InputQueue::Count: return "ERROR!"; >+ } >+ } >+} >+ >+static String jsonStringForInput(const NondeterministicInputBase& input) >+{ >+ EncodedValue encodedValue = EncodingTraits<NondeterministicInputBase>::encodeValue(input); >+ return encodedValue.asObject()->toJSONString(); >+} >+#endif // !LOG_DISABLED >+ >+static size_t offsetForInputQueue(InputQueue queue) >+{ >+ return static_cast<size_t>(queue); >+} >+ >+SegmentedInputStorage::SegmentedInputStorage() >+ : m_inputCount(0) >+{ >+ for (size_t i = 0; i < offsetForInputQueue(InputQueue::Count); i++) >+ m_queues.append(new QueuedInputs()); >+} >+ >+SegmentedInputStorage::~SegmentedInputStorage() >+{ >+ for (size_t i = 0; i < offsetForInputQueue(InputQueue::Count); i++) >+ delete m_queues.at(i); >+} >+ >+NondeterministicInputBase* SegmentedInputStorage::load(InputQueue queue, size_t offset) >+{ >+ ASSERT(queue < InputQueue::Count); >+ ASSERT(offset < m_queues[offsetForInputQueue(queue)]->size()); >+ >+ NondeterministicInputBase* input = m_queues.at(offsetForInputQueue(queue))->at(offset).get(); >+ ASSERT(input); >+ >+ LOG(WebReplay, "%-20s %s: %s %s\n", "ReplayEvents", queueTypeToLogPrefix(queue, true), input->type().string().utf8().data(), jsonStringForInput(*input).utf8().data()); >+ >+ return input; >+} >+ >+void SegmentedInputStorage::store(std::unique_ptr<NondeterministicInputBase> input) >+{ >+ ASSERT(input); >+ ASSERT(input->queue() < InputQueue::Count); >+ >+ LOG(WebReplay, "%-14s#%-5u %s: %s %s\n", "ReplayEvents", m_inputCount++, queueTypeToLogPrefix(input->queue(), false), input->type().string().utf8().data(), jsonStringForInput(*input).utf8().data()); >+ >+ m_queues.at(offsetForInputQueue(input->queue()))->append(std::move(input)); >+} >+ >+size_t SegmentedInputStorage::queueSize(InputQueue queue) const >+{ >+ return m_queues[offsetForInputQueue(queue)]->size(); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/SegmentedInputStorage.h b/Source/WebCore/replay/SegmentedInputStorage.h >new file mode 100644 >index 0000000000000000000000000000000000000000..043350b11357ba9d7e8325b85421c0df2d698b60 >--- /dev/null >+++ b/Source/WebCore/replay/SegmentedInputStorage.h >@@ -0,0 +1,61 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef SegmentedInputStorage_h >+#define SegmentedInputStorage_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <replay/NondeterministicInput.h> >+#include <wtf/Noncopyable.h> >+#include <wtf/Vector.h> >+ >+namespace WebCore { >+ >+class SegmentedInputStorage { >+ WTF_MAKE_NONCOPYABLE(SegmentedInputStorage); >+ friend class FunctorInputCursor; >+public: >+ SegmentedInputStorage(); >+ ~SegmentedInputStorage(); >+ >+ NondeterministicInputBase* load(InputQueue, size_t); >+ void store(std::unique_ptr<NondeterministicInputBase>); >+ size_t queueSize(InputQueue) const; >+ >+private: >+ typedef Vector<std::unique_ptr<NondeterministicInputBase>> QueuedInputs; >+ >+ Vector<QueuedInputs*> m_queues; >+ unsigned m_inputCount; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // SegmentedInputStorage_h >diff --git a/Source/WebCore/replay/SerializationMethods.cpp b/Source/WebCore/replay/SerializationMethods.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..b86178b54f3cc0c0be4e6f9d8c0229dc527105e8 >--- /dev/null >+++ b/Source/WebCore/replay/SerializationMethods.cpp >@@ -0,0 +1,117 @@ >+/* >+ * Copyright (C) 2012 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "SerializationMethods.h" >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include "AllReplayInputs.h" >+#include "ReplayInputTypes.h" >+#include "SecurityOrigin.h" >+#include "URL.h" >+ >+using WebCore::SecurityOrigin; >+using WebCore::URL; >+using WebCore::inputTypes; >+ >+#define IMPORT_FROM_WEBCORE_NAMESPACE(name) \ >+using WebCore::name; \ >+ >+WEB_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_WEBCORE_NAMESPACE) >+#undef IMPORT_FROM_WEBCORE_NAMESPACE >+ >+namespace JSC { >+ >+EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const NondeterministicInputBase& input) >+{ >+ EncodedValue encodedValue = EncodedValue::createObject(); >+ const AtomicString& type = input.type(); >+ encodedValue.put<String>(ASCIILiteral("type"), type.string()); >+ >+#define ENCODE_IF_TYPE_TAG_MATCHES(name) \ >+ if (type == inputTypes().name) { \ >+ InputTraits<name>::encode(encodedValue, static_cast<const name&>(input)); \ >+ return encodedValue; \ >+ } \ >+ >+ JS_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES) >+ WEB_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES) >+#undef ENCODE_IF_TYPE_TAG_MATCHES >+ >+ ASSERT_NOT_REACHED(); >+ return EncodedValue(); >+} >+ >+bool EncodingTraits<NondeterministicInputBase>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<NondeterministicInputBase>& input) >+{ >+ String type; >+ if (!encodedValue.get<String>(ASCIILiteral("type"), type)) >+ return false; >+ >+#define DECODE_IF_TYPE_TAG_MATCHES(name) \ >+ if (type == inputTypes().name) { \ >+ std::unique_ptr<name> decodedInput; \ >+ if (!InputTraits<name>::decode(encodedValue, decodedInput)) \ >+ return false; \ >+ \ >+ input = std::move(decodedInput); \ >+ return true; \ >+ } \ >+ >+ JS_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES) >+ WEB_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES) >+#undef DECODE_IF_TYPE_TAG_MATCHES >+ >+ return false; >+} >+ >+EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input) >+{ >+ return EncodedValue::createString(input->toString()); >+} >+ >+bool EncodingTraits<SecurityOrigin>::decodeValue(EncodedValue& encodedValue, RefPtr<SecurityOrigin>& input) >+{ >+ input = SecurityOrigin::createFromString(encodedValue.convertTo<String>()); >+ return true; >+} >+ >+EncodedValue EncodingTraits<URL>::encodeValue(const URL& input) >+{ >+ return EncodedValue::createString(input.string()); >+} >+ >+bool EncodingTraits<URL>::decodeValue(EncodedValue& encodedValue, URL& input) >+{ >+ input = URL(WebCore::ParsedURLString, encodedValue.convertTo<String>()); >+ return true; >+} >+ >+} // namespace JSC >+ >+#endif // ENABLE(WEB_REPLAY) >diff --git a/Source/WebCore/replay/SerializationMethods.h b/Source/WebCore/replay/SerializationMethods.h >new file mode 100644 >index 0000000000000000000000000000000000000000..8e112b820dfbd36d76dcec15314cb77886cc7439 >--- /dev/null >+++ b/Source/WebCore/replay/SerializationMethods.h >@@ -0,0 +1,72 @@ >+/* >+ * Copyright (C) 2013 University of Washington. All rights reserved. >+ * Copyright (C) 2014 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS >+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A >+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef SerializationMethods_h >+#define SerializationMethods_h >+ >+#if ENABLE(WEB_REPLAY) >+ >+#include <replay/EncodedValue.h> >+#include <replay/NondeterministicInput.h> >+ >+namespace WebCore { >+class Document; >+class Frame; >+class Page; >+class SecurityOrigin; >+class URL; >+} // namespace WebCore >+ >+// Template specializations must be defined in the same namespace as the template declaration. >+namespace JSC { >+ >+template<> struct EncodingTraits<NondeterministicInputBase> { >+ typedef NondeterministicInputBase DecodedType; >+ >+ static EncodedValue encodeValue(const NondeterministicInputBase& value); >+ static bool decodeValue(EncodedValue&, std::unique_ptr<NondeterministicInputBase>& value); >+}; >+ >+template<> struct EncodingTraits<WebCore::URL> { >+ typedef WebCore::URL DecodedType; >+ >+ static EncodedValue encodeValue(const WebCore::URL& value); >+ static bool decodeValue(EncodedValue&, WebCore::URL& value); >+}; >+ >+template<> struct EncodingTraits<WebCore::SecurityOrigin> { >+ typedef RefPtr<WebCore::SecurityOrigin> DecodedType; >+ >+ static EncodedValue encodeValue(RefPtr<WebCore::SecurityOrigin> value); >+ static bool decodeValue(EncodedValue&, RefPtr<WebCore::SecurityOrigin>& value); >+}; >+ >+} // namespace JSC >+ >+#endif // ENABLE(WEB_REPLAY) >+ >+#endif // SerializationMethods_h >diff --git a/Source/WebCore/replay/WebInputs.json b/Source/WebCore/replay/WebInputs.json >new file mode 100644 >index 0000000000000000000000000000000000000000..5e651e162894fc789bbf49ea085c017706b1f4ec >--- /dev/null >+++ b/Source/WebCore/replay/WebInputs.json >@@ -0,0 +1,69 @@ >+{ >+ "types": { >+ "Global": [ >+ { "name": "bool", "mode": "SCALAR" }, >+ { "name": "double", "mode": "SCALAR" }, >+ { "name": "int", "mode": "SCALAR" }, >+ { "name": "unsigned", "mode": "SCALAR" }, >+ { "name": "unsigned long", "mode": "SCALAR" }, >+ { "name": "unsigned long long", "mode": "SCALAR" } >+ ], >+ >+ "WTF": [ >+ { >+ "name": "String", "mode": "HEAVY_SCALAR", >+ "header": "wtf/text/WTFString.h" >+ } >+ ], >+ >+ "JavaScriptCore": [ >+ { >+ "name": "InputQueue", "mode": "SCALAR", "storage": "uint8_t", >+ "flags": ["ENUM_CLASS"], >+ "values": ["EventLoopInput", "LoaderMemoizedData", "ScriptMemoizedData", "Count"], >+ "header": "replay/NondeterministicInput.h" >+ } >+ ], >+ >+ "WebCore": [ >+ { >+ "name": "URL", "mode": "HEAVY_SCALAR", >+ "header": "platform/URL.h" >+ }, >+ { >+ "name": "SecurityOrigin", "mode": "SHARED", >+ "header": "page/SecurityOrigin.h" >+ }, >+ { >+ "name": "Page", "mode": "OWNED", >+ "header": "page/Page.h" >+ } >+ ] >+ }, >+ >+ "inputs": [ >+ { >+ "name": "BeginSegmentSentinel", >+ "description": "A sentinel input to signal the start of a segment.", >+ "queue": "EVENT_LOOP", >+ "members": [ ] >+ }, >+ { >+ "name": "EndSegmentSentinel", >+ "description": "A sentinel input to signal the end of a segment.", >+ "queue": "EVENT_LOOP", >+ "members": [ ] >+ }, >+ { >+ "name": "InitialNavigation", >+ "description": "Initiate the initial main frame navigation.", >+ "queue": "EVENT_LOOP", >+ "flags": ["HIDDEN", "CREATE_FROM_PAGE"], >+ "members": [ >+ { "name": "securityOrigin", "type": "SecurityOrigin" }, >+ { "name": "url", "type": "URL" }, >+ { "name": "referrer", "type": "String" } >+ ] >+ } >+ ] >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 128782
:
224819
|
225336