Web Replay: upstream input storage, capture/replay machinery, and inspector domain
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Mar 2014 17:01:11 +0000 (17:01 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Mar 2014 17:01:11 +0000 (17:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=128782

Source/JavaScriptCore:

Reviewed by Timothy Hatcher.

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.

Source/WebCore:

Reviewed by Timothy Hatcher, Joseph Pecoraro, and Andreas Kling.

No new tests yet, as they rely on infrastructure tracked in https://webkit.org/b/129190.

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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@164986 268f45cc-cd09-0410-ab3c-d52691b4dbfc

43 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/replay/scripts/CodeGeneratorReplayInputs.py
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/ForwardingHeaders/replay/EncodedValue.h [new file with mode: 0644]
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/inspector/InspectorController.cpp
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebCore/inspector/InspectorReplayAgent.cpp [new file with mode: 0644]
Source/WebCore/inspector/InspectorReplayAgent.h [new file with mode: 0644]
Source/WebCore/inspector/InstrumentingAgents.cpp
Source/WebCore/inspector/InstrumentingAgents.h
Source/WebCore/inspector/protocol/Replay.json [new file with mode: 0644]
Source/WebCore/make-generated-sources.sh
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/platform/Logging.h
Source/WebCore/replay/AllReplayInputs.h [new file with mode: 0644]
Source/WebCore/replay/CapturingInputCursor.cpp [new file with mode: 0644]
Source/WebCore/replay/CapturingInputCursor.h [new file with mode: 0644]
Source/WebCore/replay/EventLoopInput.h
Source/WebCore/replay/EventLoopInputDispatcher.cpp [new file with mode: 0644]
Source/WebCore/replay/EventLoopInputDispatcher.h [new file with mode: 0644]
Source/WebCore/replay/FunctorInputCursor.h [new file with mode: 0644]
Source/WebCore/replay/ReplayController.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplayController.h [new file with mode: 0644]
Source/WebCore/replay/ReplayInputCreationMethods.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplayInputDispatchMethods.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplayInputTypes.cpp
Source/WebCore/replay/ReplayInputTypes.h
Source/WebCore/replay/ReplaySession.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplaySession.h [new file with mode: 0644]
Source/WebCore/replay/ReplaySessionSegment.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplaySessionSegment.h [new file with mode: 0644]
Source/WebCore/replay/ReplayingInputCursor.cpp [new file with mode: 0644]
Source/WebCore/replay/ReplayingInputCursor.h [new file with mode: 0644]
Source/WebCore/replay/SegmentedInputStorage.cpp [new file with mode: 0644]
Source/WebCore/replay/SegmentedInputStorage.h [new file with mode: 0644]
Source/WebCore/replay/SerializationMethods.cpp [new file with mode: 0644]
Source/WebCore/replay/SerializationMethods.h [new file with mode: 0644]
Source/WebCore/replay/WebInputs.json [new file with mode: 0644]

index 2af77fa..c068df8 100644 (file)
@@ -1,3 +1,22 @@
+2014-03-03  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 Timothy Hatcher.
+
+        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-03-02  Filip Pizlo  <fpizlo@apple.com>
 
         PolymorphicPutByIdList should have a simpler construction API with basically a single entrypoint
index 46f1fb9..87e2aaf 100644 (file)
                978801411471AD920041B016 /* JSDateMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 9788FC231471AD0C0068CE2D /* JSDateMath.h */; 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, ); }; };
+               99CC0B6218BE9946006CEBCC /* CodeGeneratorReplayInputsTemplates.py in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1E18A1B1E70026D88F /* CodeGeneratorReplayInputsTemplates.py */; settings = {ATTRIBUTES = (Private, ); }; };
+               99CC0B6318BE9950006CEBCC /* CodeGeneratorReplayInputs.py in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1D18A1B1E70026D88F /* CodeGeneratorReplayInputs.py */; settings = {ATTRIBUTES = (Private, ); }; };
                99E45A2418A1B2590026D88F /* EmptyInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1F18A1B2590026D88F /* EmptyInputCursor.h */; settings = {ATTRIBUTES = (Private, ); }; };
                99E45A2518A1B2590026D88F /* EncodedValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99E45A2018A1B2590026D88F /* EncodedValue.cpp */; };
                99E45A2618A1B2590026D88F /* EncodedValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A2118A1B2590026D88F /* EncodedValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
                                86EBF3041560F06A008E9222 /* NamePrototype.h in Headers */,
                                BC02E9110E1839DB000F9297 /* NativeErrorConstructor.h in Headers */,
                                BC02E9130E1839DB000F9297 /* NativeErrorPrototype.h in Headers */,
+                               99CC0B6318BE9950006CEBCC /* CodeGeneratorReplayInputs.py in Headers */,
                                0FFB922016D033B70055A5DB /* NodeConstructors.h in Headers */,
                                7EFF00640EC05A9A00AA7C93 /* NodeInfo.h in Headers */,
                                BC18C43F0E16F5CD00B34460 /* Nodes.h in Headers */,
                                BC18C4420E16F5CD00B34460 /* NumberConstructor.lut.h in Headers */,
                                BC18C4430E16F5CD00B34460 /* NumberObject.h in Headers */,
                                BC18C4440E16F5CD00B34460 /* NumberPrototype.h in Headers */,
+                               99CC0B6218BE9946006CEBCC /* CodeGeneratorReplayInputsTemplates.py in Headers */,
                                142D3939103E4560007DCB52 /* NumericStrings.h in Headers */,
                                86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */,
                                86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */,
index e3c8199..cd65108 100644 (file)
@@ -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:
index e20fae7..13475ae 100644 (file)
@@ -1,3 +1,273 @@
+2014-03-03  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 Timothy Hatcher, Joseph Pecoraro, and Andreas Kling.
+
+        No new tests yet, as they rely on infrastructure tracked in https://webkit.org/b/129190.
+
+        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-03-03  Antoine Quint  <graouts@webkit.org>
 
         Respect SVG fragment identifiers in <img> src attribute
index 555dd50..3d2b8cb 100644 (file)
@@ -1109,6 +1109,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 \
 #
@@ -1142,6 +1143,22 @@ CommandLineAPIModuleSource.h : CommandLineAPIModuleSource.js
        perl $(InspectorScripts)/xxd.pl CommandLineAPIModuleSource_js ./CommandLineAPIModuleSource.min.js CommandLineAPIModuleSource.h
        rm -f ./CommandLineAPIModuleSource.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 (file)
index 0000000..5e7ce63
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_EncodedValue_h
+#define WebCore_FWD_EncodedValue_h
+#include <JavaScriptCore/EncodedValue.h>
+#endif
index 90c851d..698bd42 100644 (file)
                990A1A0518ADA48400183FD1 /* ReplayInputTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 990A19F518ADA48400183FD1 /* ReplayInputTypes.h */; };
                9920398218B95BC600B39AF9 /* UserInputBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9920398018B95BC600B39AF9 /* UserInputBridge.cpp */; };
                9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 9920398118B95BC600B39AF9 /* UserInputBridge.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               99CC0B4D18BE9849006CEBCC /* AllReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B3818BE9849006CEBCC /* AllReplayInputs.h */; };
+               99CC0B4E18BE9849006CEBCC /* CapturingInputCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B3918BE9849006CEBCC /* CapturingInputCursor.cpp */; };
+               99CC0B4F18BE9849006CEBCC /* CapturingInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B3A18BE9849006CEBCC /* CapturingInputCursor.h */; };
+               99CC0B5018BE9849006CEBCC /* EventLoopInputDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B3B18BE9849006CEBCC /* EventLoopInputDispatcher.cpp */; };
+               99CC0B5118BE9849006CEBCC /* EventLoopInputDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B3C18BE9849006CEBCC /* EventLoopInputDispatcher.h */; };
+               99CC0B5218BE9849006CEBCC /* FunctorInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B3D18BE9849006CEBCC /* FunctorInputCursor.h */; };
+               99CC0B5318BE9849006CEBCC /* ReplayController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B3E18BE9849006CEBCC /* ReplayController.cpp */; };
+               99CC0B5418BE9849006CEBCC /* ReplayController.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B3F18BE9849006CEBCC /* ReplayController.h */; };
+               99CC0B5518BE9849006CEBCC /* ReplayingInputCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4018BE9849006CEBCC /* ReplayingInputCursor.cpp */; };
+               99CC0B5618BE984A006CEBCC /* ReplayingInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B4118BE9849006CEBCC /* ReplayingInputCursor.h */; };
+               99CC0B5718BE984A006CEBCC /* ReplayInputCreationMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4218BE9849006CEBCC /* ReplayInputCreationMethods.cpp */; };
+               99CC0B5818BE984A006CEBCC /* ReplayInputDispatchMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4318BE9849006CEBCC /* ReplayInputDispatchMethods.cpp */; };
+               99CC0B5918BE984A006CEBCC /* ReplaySession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4418BE9849006CEBCC /* ReplaySession.cpp */; };
+               99CC0B5A18BE984A006CEBCC /* ReplaySession.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B4518BE9849006CEBCC /* ReplaySession.h */; };
+               99CC0B5B18BE984A006CEBCC /* ReplaySessionSegment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4618BE9849006CEBCC /* ReplaySessionSegment.cpp */; };
+               99CC0B5C18BE984A006CEBCC /* ReplaySessionSegment.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B4718BE9849006CEBCC /* ReplaySessionSegment.h */; };
+               99CC0B5D18BE984A006CEBCC /* SegmentedInputStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4818BE9849006CEBCC /* SegmentedInputStorage.cpp */; };
+               99CC0B5E18BE984A006CEBCC /* SegmentedInputStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B4918BE9849006CEBCC /* SegmentedInputStorage.h */; };
+               99CC0B5F18BE984A006CEBCC /* SerializationMethods.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B4A18BE9849006CEBCC /* SerializationMethods.cpp */; };
+               99CC0B6018BE984A006CEBCC /* SerializationMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B4B18BE9849006CEBCC /* SerializationMethods.h */; };
+               99CC0B6118BE984A006CEBCC /* WebInputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 99CC0B4C18BE9849006CEBCC /* WebInputs.json */; };
+               99CC0B6618BE9F15006CEBCC /* InspectorReplayAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B6418BE9F15006CEBCC /* InspectorReplayAgent.cpp */; };
+               99CC0B6718BE9F15006CEBCC /* InspectorReplayAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B6518BE9F15006CEBCC /* InspectorReplayAgent.h */; };
+               99CC0B6A18BEA1FF006CEBCC /* WebReplayInputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99CC0B6818BEA1FF006CEBCC /* WebReplayInputs.cpp */; };
+               99CC0B6B18BEA1FF006CEBCC /* WebReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 99CC0B6918BEA1FF006CEBCC /* WebReplayInputs.h */; };
                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 */; };
                990A19F518ADA48400183FD1 /* ReplayInputTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayInputTypes.h; sourceTree = "<group>"; };
                9920398018B95BC600B39AF9 /* UserInputBridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserInputBridge.cpp; sourceTree = "<group>"; };
                9920398118B95BC600B39AF9 /* UserInputBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserInputBridge.h; sourceTree = "<group>"; };
+               99CC0B3818BE9849006CEBCC /* AllReplayInputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllReplayInputs.h; sourceTree = "<group>"; };
+               99CC0B3918BE9849006CEBCC /* CapturingInputCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CapturingInputCursor.cpp; sourceTree = "<group>"; };
+               99CC0B3A18BE9849006CEBCC /* CapturingInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CapturingInputCursor.h; sourceTree = "<group>"; };
+               99CC0B3B18BE9849006CEBCC /* EventLoopInputDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoopInputDispatcher.cpp; sourceTree = "<group>"; };
+               99CC0B3C18BE9849006CEBCC /* EventLoopInputDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoopInputDispatcher.h; sourceTree = "<group>"; };
+               99CC0B3D18BE9849006CEBCC /* FunctorInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FunctorInputCursor.h; sourceTree = "<group>"; };
+               99CC0B3E18BE9849006CEBCC /* ReplayController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayController.cpp; sourceTree = "<group>"; };
+               99CC0B3F18BE9849006CEBCC /* ReplayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayController.h; sourceTree = "<group>"; };
+               99CC0B4018BE9849006CEBCC /* ReplayingInputCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayingInputCursor.cpp; sourceTree = "<group>"; };
+               99CC0B4118BE9849006CEBCC /* ReplayingInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayingInputCursor.h; sourceTree = "<group>"; };
+               99CC0B4218BE9849006CEBCC /* ReplayInputCreationMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayInputCreationMethods.cpp; sourceTree = "<group>"; };
+               99CC0B4318BE9849006CEBCC /* ReplayInputDispatchMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayInputDispatchMethods.cpp; sourceTree = "<group>"; };
+               99CC0B4418BE9849006CEBCC /* ReplaySession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaySession.cpp; sourceTree = "<group>"; };
+               99CC0B4518BE9849006CEBCC /* ReplaySession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaySession.h; sourceTree = "<group>"; };
+               99CC0B4618BE9849006CEBCC /* ReplaySessionSegment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaySessionSegment.cpp; sourceTree = "<group>"; };
+               99CC0B4718BE9849006CEBCC /* ReplaySessionSegment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaySessionSegment.h; sourceTree = "<group>"; };
+               99CC0B4818BE9849006CEBCC /* SegmentedInputStorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentedInputStorage.cpp; sourceTree = "<group>"; };
+               99CC0B4918BE9849006CEBCC /* SegmentedInputStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SegmentedInputStorage.h; sourceTree = "<group>"; };
+               99CC0B4A18BE9849006CEBCC /* SerializationMethods.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerializationMethods.cpp; sourceTree = "<group>"; };
+               99CC0B4B18BE9849006CEBCC /* SerializationMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerializationMethods.h; sourceTree = "<group>"; };
+               99CC0B4C18BE9849006CEBCC /* WebInputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = WebInputs.json; sourceTree = "<group>"; };
+               99CC0B6418BE9F15006CEBCC /* InspectorReplayAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorReplayAgent.cpp; sourceTree = "<group>"; };
+               99CC0B6518BE9F15006CEBCC /* InspectorReplayAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorReplayAgent.h; sourceTree = "<group>"; };
+               99CC0B6818BEA1FF006CEBCC /* WebReplayInputs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebReplayInputs.cpp; sourceTree = "<group>"; };
+               99CC0B6918BEA1FF006CEBCC /* WebReplayInputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebReplayInputs.h; 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>"; };
                                4F6FDD631341DEDD001F8EE3 /* InspectorPageAgent.h */,
                                9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */,
                                9F0D6B2D121BFEBA006C0288 /* InspectorProfilerAgent.h */,
+                               99CC0B6418BE9F15006CEBCC /* InspectorReplayAgent.cpp */,
+                               99CC0B6518BE9F15006CEBCC /* InspectorReplayAgent.h */,
                                82AB1771125C826700C5069D /* InspectorResourceAgent.cpp */,
                                82AB1772125C826700C5069D /* InspectorResourceAgent.h */,
                                82AB176F125C826700C5069D /* InspectorStyleSheet.cpp */,
                51C4AA5118B28357007BFE9B /* mac */ = {
                        isa = PBXGroup;
                        children = (
+                               510192D818B7D7AB007FC7A1 /* imageControlsMac.css */,
                                510192CF18B6B9AB007FC7A1 /* ImageControlsRootElementMac.cpp */,
                                510192D018B6B9AB007FC7A1 /* ImageControlsRootElementMac.h */,
-                               510192D818B7D7AB007FC7A1 /* imageControlsMac.css */,
                        );
                        path = mac;
                        sourceTree = "<group>";
                                656581AF09D14EE6000E61D7 /* UserAgentStyleSheetsData.cpp */,
                                1A1414B313A0F0500019996C /* WebKitFontFamilyNames.cpp */,
                                1A1414B413A0F0500019996C /* WebKitFontFamilyNames.h */,
+                               99CC0B6818BEA1FF006CEBCC /* WebReplayInputs.cpp */,
+                               99CC0B6918BEA1FF006CEBCC /* WebReplayInputs.h */,
                                656581EA09D1508D000E61D7 /* XLinkNames.cpp */,
                                656581EB09D1508D000E61D7 /* XLinkNames.h */,
                                A833C80A0A2CF25600D57664 /* XMLNames.cpp */,
                                975CA287130365F800E99AD9 /* Crypto.cpp */,
                                975CA288130365F800E99AD9 /* Crypto.h */,
                                975CA289130365F800E99AD9 /* Crypto.idl */,
+                               1AF4CEE718BC350100BC2D34 /* DefaultVisitedLinkStore.cpp */,
+                               1AF4CEE818BC350100BC2D34 /* DefaultVisitedLinkStore.h */,
                                CCC2B51015F613060048CDD6 /* DeviceClient.h */,
                                CCC2B51115F613060048CDD6 /* DeviceController.cpp */,
                                CCC2B51215F613060048CDD6 /* DeviceController.h */,
                                517FBA18151AA71B00B57959 /* DOMWindowExtension.h */,
                                97D2AD0114B823A60093DF32 /* DOMWindowProperty.cpp */,
                                97D2AD0214B823A60093DF32 /* DOMWindowProperty.h */,
-                               1AF4CEE718BC350100BC2D34 /* DefaultVisitedLinkStore.cpp */,
-                               1AF4CEE818BC350100BC2D34 /* DefaultVisitedLinkStore.h */,
                                A718760D0B2A120100A16ECE /* DragActions.h */,
                                A7CA59620B27C1F200FA021D /* DragClient.h */,
                                A7CA595C0B27BD9E00FA021D /* DragController.cpp */,
                99E45A1318A021760026D88F /* replay */ = {
                        isa = PBXGroup;
                        children = (
+                               99CC0B3818BE9849006CEBCC /* AllReplayInputs.h */,
+                               99CC0B3918BE9849006CEBCC /* CapturingInputCursor.cpp */,
+                               99CC0B3A18BE9849006CEBCC /* CapturingInputCursor.h */,
                                99E45A1618A063BE0026D88F /* EventLoopInput.h */,
+                               99CC0B3B18BE9849006CEBCC /* EventLoopInputDispatcher.cpp */,
+                               99CC0B3C18BE9849006CEBCC /* EventLoopInputDispatcher.h */,
+                               99CC0B3D18BE9849006CEBCC /* FunctorInputCursor.h */,
+                               99CC0B3E18BE9849006CEBCC /* ReplayController.cpp */,
+                               99CC0B3F18BE9849006CEBCC /* ReplayController.h */,
+                               99CC0B4018BE9849006CEBCC /* ReplayingInputCursor.cpp */,
+                               99CC0B4118BE9849006CEBCC /* ReplayingInputCursor.h */,
+                               99CC0B4218BE9849006CEBCC /* ReplayInputCreationMethods.cpp */,
+                               99CC0B4318BE9849006CEBCC /* ReplayInputDispatchMethods.cpp */,
                                990A19F418ADA48400183FD1 /* ReplayInputTypes.cpp */,
                                990A19F518ADA48400183FD1 /* ReplayInputTypes.h */,
+                               99CC0B4418BE9849006CEBCC /* ReplaySession.cpp */,
+                               99CC0B4518BE9849006CEBCC /* ReplaySession.h */,
+                               99CC0B4618BE9849006CEBCC /* ReplaySessionSegment.cpp */,
+                               99CC0B4718BE9849006CEBCC /* ReplaySessionSegment.h */,
+                               99CC0B4818BE9849006CEBCC /* SegmentedInputStorage.cpp */,
+                               99CC0B4918BE9849006CEBCC /* SegmentedInputStorage.h */,
+                               99CC0B4A18BE9849006CEBCC /* SerializationMethods.cpp */,
+                               99CC0B4B18BE9849006CEBCC /* SerializationMethods.h */,
                                9920398018B95BC600B39AF9 /* UserInputBridge.cpp */,
                                9920398118B95BC600B39AF9 /* UserInputBridge.h */,
+                               99CC0B4C18BE9849006CEBCC /* WebInputs.json */,
                        );
                        path = replay;
                        sourceTree = "<group>";
                A5C974CE11485FDA0066F2AB /* cocoa */ = {
                        isa = PBXGroup;
                        children = (
-                               7CC564B918BAC720001B9652 /* TelephoneNumberDetectorCocoa.cpp */,
                                A5C974CF11485FF10066F2AB /* KeyEventCocoa.h */,
                                A5C974D011485FF10066F2AB /* KeyEventCocoa.mm */,
+                               7CC564B918BAC720001B9652 /* TelephoneNumberDetectorCocoa.cpp */,
                        );
                        path = cocoa;
                        sourceTree = "<group>";
                                BCF7E491137CD7C7001DDAE7 /* AdjustViewSizeOrNot.h in Headers */,
                                84D0C4061115F1EA0018AA34 /* AffineTransform.h in Headers */,
                                0705850817FA4689005F2BCB /* AllAudioCapabilities.h in Headers */,
+                               99CC0B4D18BE9849006CEBCC /* AllReplayInputs.h in Headers */,
                                073AB4B317F8BACA006E0D6F /* AllVideoCapabilities.h in Headers */,
                                CEDA12D7152CA1CB00D9E08D /* AlternativeTextClient.h in Headers */,
                                CE08C3D2152B599A0021B8C2 /* AlternativeTextController.h in Headers */,
                                07C59B7617F7D0DB000FBCBB /* CapabilityRange.h in Headers */,
                                079D0868162F20E800DB8658 /* CaptionUserPreferences.h in Headers */,
                                079D086B162F21F900DB8658 /* CaptionUserPreferencesMediaAF.h in Headers */,
+                               99CC0B4F18BE9849006CEBCC /* CapturingInputCursor.h in Headers */,
                                6550B69E099DF0270090D781 /* CDATASection.h in Headers */,
                                CDDD571518B57A8200A94FCB /* CDMSession.h in Headers */,
                                5FA904CA178E61F5004C8A2D /* CertificateInfo.h in Headers */,
                                97627B8E14FB3CEE002CDCA1 /* ContextDestructionObserver.h in Headers */,
                                93B6A0E60B0BCA5C00F5027A /* ContextMenu.h in Headers */,
                                065AD4F50B0C2EDA005A2B1D /* ContextMenuClient.h in Headers */,
+                               5106D7BE18BDB76F000AB166 /* ContextMenuContext.h in Headers */,
                                065AD4F70B0C2EDA005A2B1D /* ContextMenuController.h in Headers */,
                                06027CAD0B1CBFC000884B2D /* ContextMenuItem.h in Headers */,
                                7ADE722610CBBB9B006B3B3A /* ContextMenuProvider.h in Headers */,
                                41A3D58F101C152D00316D07 /* DedicatedWorkerThread.h in Headers */,
                                FD06DFA6134A4DEF006F5D7D /* DefaultAudioDestinationNode.h in Headers */,
                                4167EBF6102962BA003D252A /* DefaultSharedWorkerRepository.h in Headers */,
+                               1AF4CEEA18BC350100BC2D34 /* DefaultVisitedLinkStore.h in Headers */,
                                FD31602C12B0267600C1A359 /* DelayDSPKernel.h in Headers */,
                                FD31602E12B0267600C1A359 /* DelayNode.h in Headers */,
                                FD31603112B0267600C1A359 /* DelayProcessor.h in Headers */,
                                AD4495F4141FC08900541EDF /* EventListenerMap.h in Headers */,
                                1CA19E160DC255CA0065A994 /* EventLoop.h in Headers */,
                                99E45A1718A063BE0026D88F /* EventLoopInput.h in Headers */,
+                               99CC0B5118BE9849006CEBCC /* EventLoopInputDispatcher.h in Headers */,
                                939885C408B7E3D100E707C4 /* EventNames.h in Headers */,
                                8F67561B1288B17B0047ACA3 /* EventQueue.h in Headers */,
                                E0FEF372B17C53EAC1C1FBEE /* EventSource.h in Headers */,
                                97205AB0123928CA00B17380 /* FTPDirectoryDocument.h in Headers */,
                                51C81B8A0C4422F70019ECE3 /* FTPDirectoryParser.h in Headers */,
                                26B999931803B9D900D01121 /* FunctionCall.h in Headers */,
+                               99CC0B5218BE9849006CEBCC /* FunctorInputCursor.h in Headers */,
                                FD31600D12B0267600C1A359 /* GainNode.h in Headers */,
                                935C477509AC4D8E00A6AAB4 /* GapRects.h in Headers */,
                                1432E8470C51493800B1500F /* GCController.h in Headers */,
                                977B3867122883E900B81FF8 /* HTMLDocumentParser.h in Headers */,
                                93309DE8099E64920056E581 /* htmlediting.h in Headers */,
                                93F198E608245E59001E9ABC /* HTMLElement.h in Headers */,
-                               9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */,
                                A17C81230F2A5CF7005DAAEB /* HTMLElementFactory.h in Headers */,
                                977B37241228721700B81FF8 /* HTMLElementStack.h in Headers */,
                                B562DB6017D3CD630010AF96 /* HTMLElementTypeHelpers.h in Headers */,
                                978AD67514130A8D00C7CAE3 /* HTMLSpanElement.h in Headers */,
                                A871DC230A15205700B12A68 /* HTMLStyleElement.h in Headers */,
                                D3D4E973130C7CFE007BA540 /* HTMLSummaryElement.h in Headers */,
+                               99CC0B6B18BEA1FF006CEBCC /* WebReplayInputs.h in Headers */,
                                A871DB2B0A150BD600B12A68 /* HTMLTableCaptionElement.h in Headers */,
                                A871DB2A0A150BD600B12A68 /* HTMLTableCellElement.h in Headers */,
                                A871DB2F0A150BD600B12A68 /* HTMLTableColElement.h in Headers */,
                                0F03C0751884805500A5F8CA /* InspectorOverlay.h in Headers */,
                                4F6FDD651341DEDD001F8EE3 /* InspectorPageAgent.h in Headers */,
                                9F0D6B2F121BFEBA006C0288 /* InspectorProfilerAgent.h in Headers */,
+                               99CC0B6718BE9F15006CEBCC /* InspectorReplayAgent.h in Headers */,
                                82AB1776125C826700C5069D /* InspectorResourceAgent.h in Headers */,
                                82AB1774125C826700C5069D /* InspectorStyleSheet.h in Headers */,
                                82889B4D13C62392009A6156 /* InspectorStyleTextEditor.h in Headers */,
                                FDA15EBE12B03F0B003A583A /* JSConvolverNode.h in Headers */,
                                FE6FD48E0F676E9300092873 /* JSCoordinates.h in Headers */,
                                930705DA09E0C9BF00B17FE4 /* JSCounter.h in Headers */,
-                               1AF4CEEA18BC350100BC2D34 /* DefaultVisitedLinkStore.h in Headers */,
                                975CA2A21303679D00E99AD9 /* JSCrypto.h in Headers */,
                                E157A8F118185425009F821D /* JSCryptoAlgorithmBuilder.h in Headers */,
                                E1C657131815F9DD00256CDD /* JSCryptoAlgorithmDictionary.h in Headers */,
                                1AE2AA210A1CDAB400B42B25 /* JSHTMLBaseFontElement.h in Headers */,
                                1AE2AA230A1CDAB400B42B25 /* JSHTMLBodyElement.h in Headers */,
                                1AE2AA250A1CDAB400B42B25 /* JSHTMLBRElement.h in Headers */,
-                               7CC564B818BABEA6001B9652 /* TelephoneNumberDetector.h in Headers */,
                                A80E7E9F0A1A83E3007FB8C5 /* JSHTMLButtonElement.h in Headers */,
                                938E666209F09B87008A48EC /* JSHTMLCanvasElement.h in Headers */,
                                BCCBAD410C18C14200CE890F /* JSHTMLCollection.h in Headers */,
                                450CEBF115073BBE002BB149 /* LabelableElement.h in Headers */,
                                A456FA2711AD4A830020B420 /* LabelsNodeList.h in Headers */,
                                85EC9AFB0A71A2C600EEEAED /* Language.h in Headers */,
-                               5106D7BE18BDB76F000AB166 /* ContextMenuContext.h in Headers */,
                                2917B5621473496C0052C9D0 /* LayerFlushScheduler.h in Headers */,
                                2917B5631473496C0052C9D0 /* LayerFlushSchedulerClient.h in Headers */,
                                93F72AF31666EDFC002A02BD /* LayerPool.h in Headers */,
                                A871DFE40A15376B00B12A68 /* RenderWidget.h in Headers */,
                                A89CCC530F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.h in Headers */,
                                93309E0A099E64920056E581 /* ReplaceSelectionCommand.h in Headers */,
+                               99CC0B5418BE9849006CEBCC /* ReplayController.h in Headers */,
+                               99CC0B5618BE984A006CEBCC /* ReplayingInputCursor.h in Headers */,
                                990A1A0518ADA48400183FD1 /* ReplayInputTypes.h in Headers */,
+                               99CC0B5A18BE984A006CEBCC /* ReplaySession.h in Headers */,
+                               99CC0B5C18BE984A006CEBCC /* ReplaySessionSegment.h in Headers */,
                                4998AEC613F9D0EA0090B1AA /* RequestAnimationFrameCallback.h in Headers */,
                                F55B3DD01251F12D003EF269 /* ResetInputType.h in Headers */,
                                514BC843161CF05C004D52F4 /* ResourceBuffer.h in Headers */,
                                974D2DA5146A535D00D51F8B /* SecurityPolicy.h in Headers */,
                                2D5BC42716F882EE007048D0 /* SecurityPolicyViolationEvent.h in Headers */,
                                371F4FFC0D25E7F300ECE0D5 /* SegmentedFontData.h in Headers */,
+                               99CC0B5E18BE984A006CEBCC /* SegmentedInputStorage.h in Headers */,
                                B2C3DA2F0D006C1D00EF6F26 /* SegmentedString.h in Headers */,
                                BEA807C90F714A0300524199 /* SelectionRect.h in Headers */,
                                E44B4BB4141650D7002B1D8B /* SelectorChecker.h in Headers */,
                                26B999971804D54200D01121 /* SelectorCompiler.h in Headers */,
                                415071581685067300C3C7B3 /* SelectorFilter.h in Headers */,
                                E45322AC140CE267005A0F92 /* SelectorQuery.h in Headers */,
+                               99CC0B6018BE984A006CEBCC /* SerializationMethods.h in Headers */,
                                E18DF33518AAF12C00773E59 /* SerializedCryptoKeyWrap.h in Headers */,
                                A75E497610752ACB00C9B896 /* SerializedScriptValue.h in Headers */,
                                756B2CE118B7101600FECFAA /* SessionID.h in Headers */,
                                A8CFF0510A154F09000A4234 /* TableLayout.h in Headers */,
                                BCE3BEC30D222B1D007E06E4 /* TagNodeList.h in Headers */,
                                F55B3DD61251F12D003EF269 /* TelephoneInputType.h in Headers */,
+                               7CC564B818BABEA6001B9652 /* TelephoneNumberDetector.h in Headers */,
                                C65046A9167BFB5500CC2A4D /* TemplateContentDocumentFragment.h in Headers */,
                                6550B6A6099DF0270090D781 /* Text.h in Headers */,
                                93309E17099E64920056E581 /* TextAffinity.h in Headers */,
                                003F1FEA11E6AB43008258D9 /* UserContentTypes.h in Headers */,
                                BCACF3BD1072921A00C0C8A3 /* UserContentURLPattern.h in Headers */,
                                2542F4DB1166C25A00E89A86 /* UserGestureIndicator.h in Headers */,
+                               9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */,
                                078E092E17D14D1C00420AA1 /* UserMediaClient.h in Headers */,
                                078E092F17D14D1C00420AA1 /* UserMediaController.h in Headers */,
                                078E093017D14D1C00420AA1 /* UserMediaRequest.h in Headers */,
                                93309E20099E64920056E581 /* VisiblePosition.h in Headers */,
                                A883DF280F3D045D00F19BF6 /* VisibleSelection.h in Headers */,
                                93309E1E099E64920056E581 /* VisibleUnits.h in Headers */,
-                               1ABA80001897341200DCE9D6 /* VisitedLinkStore.h in Headers */,
                                419BC2DF1685329900D64D6D /* VisitedLinkState.h in Headers */,
+                               1ABA80001897341200DCE9D6 /* VisitedLinkStore.h in Headers */,
                                E44613B60CD6344E00FADA75 /* VoidCallback.h in Headers */,
                                BE20507A18A4586B0080647E /* VTTCue.h in Headers */,
                                A14832B0187F618D00DA63A6 /* WAKAppKitStubs.h in Headers */,
                                9370918D1416D86B00477333 /* textAreaResizeCorner@2x.png in Resources */,
                                46D4F24B0AF97E810035385A /* verticalTextCursor.png in Resources */,
                                85136CA70AED665900F90A3D /* waitCursor.png in Resources */,
+                               99CC0B6118BE984A006CEBCC /* WebInputs.json in Resources */,
                                85136CA80AED665900F90A3D /* westResizeCursor.png in Resources */,
                                1AB1AE7A0C051FDE00139F4F /* zoomInCursor.png in Resources */,
                                1AB1AE7B0C051FDE00139F4F /* zoomOutCursor.png in Resources */,
                        );
                        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 */
 
                                85031B3C0A44EFC700F992E0 /* BeforeUnloadEvent.cpp in Sources */,
                                B2C3DA230D006C1D00EF6F26 /* BidiContext.cpp in Sources */,
                                BCE7898B1120E8020060ECE5 /* BidiRun.cpp in Sources */,
-                               5106D7BD18BDB76F000AB166 /* ContextMenuContext.cpp in Sources */,
                                FD31608C12B026F700C1A359 /* Biquad.cpp in Sources */,
                                FD31602412B0267600C1A359 /* BiquadDSPKernel.cpp in Sources */,
                                FDC54F041399B0DA008D9117 /* BiquadFilterNode.cpp in Sources */,
                                070584FF17F9F05E005F2BCB /* CapabilityRange.cpp in Sources */,
                                072CA86116CB4DC3008AE131 /* CaptionUserPreferences.cpp in Sources */,
                                079D086C162F21F900DB8658 /* CaptionUserPreferencesMediaAF.cpp in Sources */,
+                               99CC0B4E18BE9849006CEBCC /* CapturingInputCursor.cpp in Sources */,
                                6550B69D099DF0270090D781 /* CDATASection.cpp in Sources */,
                                CDA98E0B1603CD6000FEA3B1 /* CDM.cpp in Sources */,
                                CDCA98EB18B2C8EB00C12FF9 /* CDMPrivateMediaPlayer.cpp in Sources */,
                                97C471DB12F925BD0086354B /* ContentSecurityPolicy.cpp in Sources */,
                                41D015CB0F4B5C71004A662F /* ContentType.cpp in Sources */,
                                97627B8D14FB3CEE002CDCA1 /* ContextDestructionObserver.cpp in Sources */,
+                               5106D7BD18BDB76F000AB166 /* ContextMenuContext.cpp in Sources */,
                                065AD4F60B0C2EDA005A2B1D /* ContextMenuController.cpp in Sources */,
                                06027CB30B1CC03D00884B2D /* ContextMenuItemMac.mm in Sources */,
                                93B6A0EA0B0BCA8400F5027A /* ContextMenuMac.mm in Sources */,
                                41A3D58E101C152D00316D07 /* DedicatedWorkerThread.cpp in Sources */,
                                FD06DFA5134A4DEF006F5D7D /* DefaultAudioDestinationNode.cpp in Sources */,
                                4167EBF5102962BA003D252A /* DefaultSharedWorkerRepository.cpp in Sources */,
+                               1AF4CEE918BC350100BC2D34 /* DefaultVisitedLinkStore.cpp in Sources */,
                                FD31602B12B0267600C1A359 /* DelayDSPKernel.cpp in Sources */,
                                FD31602D12B0267600C1A359 /* DelayNode.cpp in Sources */,
                                FD31603012B0267600C1A359 /* DelayProcessor.cpp in Sources */,
                                FE6938B61045D67E008EABB6 /* EventHandlerIOS.mm in Sources */,
                                93C09A7F0B064EEF005ABD4D /* EventHandlerMac.mm in Sources */,
                                AD4495F3141FC08900541EDF /* EventListenerMap.cpp in Sources */,
+                               99CC0B5018BE9849006CEBCC /* EventLoopInputDispatcher.cpp in Sources */,
                                26F40D4A14904A6300CA67C4 /* EventLoopIOS.mm in Sources */,
                                1CA19E050DC255950065A994 /* EventLoopMac.mm in Sources */,
                                939885C308B7E3D100E707C4 /* EventNames.cpp in Sources */,
                                7C522D4B15B477E8009B7C95 /* InspectorOverlay.cpp in Sources */,
                                4F6FDD641341DEDD001F8EE3 /* InspectorPageAgent.cpp in Sources */,
                                9F0D6B2E121BFEBA006C0288 /* InspectorProfilerAgent.cpp in Sources */,
+                               99CC0B6618BE9F15006CEBCC /* InspectorReplayAgent.cpp in Sources */,
                                82AB1775125C826700C5069D /* InspectorResourceAgent.cpp in Sources */,
                                82AB1773125C826700C5069D /* InspectorStyleSheet.cpp in Sources */,
                                82889B4C13C62392009A6156 /* InspectorStyleTextEditor.cpp in Sources */,
                                8542A7960AE5C94200DF58DF /* JSSVGPoint.cpp in Sources */,
                                B2FA3DE20AB75A6F000E5AC4 /* JSSVGPointList.cpp in Sources */,
                                B2FA3DE50AB75A6F000E5AC4 /* JSSVGPolygonElement.cpp in Sources */,
-                               7CC564BA18BAC720001B9652 /* TelephoneNumberDetectorCocoa.cpp in Sources */,
                                B2FA3DE70AB75A6F000E5AC4 /* JSSVGPolylineElement.cpp in Sources */,
                                B2FA3DE90AB75A6F000E5AC4 /* JSSVGPreserveAspectRatio.cpp in Sources */,
                                B2FA3DEB0AB75A6F000E5AC4 /* JSSVGRadialGradientElement.cpp in Sources */,
                                1ADA14100E1AE5D900023EE5 /* PluginMainThreadScheduler.cpp in Sources */,
                                71E2183B17359FB8006E6E4D /* PlugInsResourcesData.cpp in Sources */,
                                76FF17E311235673001D61B5 /* PluginViewNone.cpp in Sources */,
-                               9920398218B95BC600B39AF9 /* UserInputBridge.cpp in Sources */,
                                B2B1F7160D00CAA8004AEA64 /* PointerEventsHitRules.cpp in Sources */,
                                A1E1154613015C4E0054AC8C /* PointLightSource.cpp in Sources */,
                                97059977107D975200A50A7C /* PolicyCallback.cpp in Sources */,
                                A871DFE50A15376B00B12A68 /* RenderWidget.cpp in Sources */,
                                A89CCC520F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.cpp in Sources */,
                                93309E09099E64920056E581 /* ReplaceSelectionCommand.cpp in Sources */,
+                               99CC0B5318BE9849006CEBCC /* ReplayController.cpp in Sources */,
+                               99CC0B5518BE9849006CEBCC /* ReplayingInputCursor.cpp in Sources */,
+                               99CC0B5718BE984A006CEBCC /* ReplayInputCreationMethods.cpp in Sources */,
+                               99CC0B5818BE984A006CEBCC /* ReplayInputDispatchMethods.cpp in Sources */,
                                990A1A0418ADA48400183FD1 /* ReplayInputTypes.cpp in Sources */,
+                               99CC0B5918BE984A006CEBCC /* ReplaySession.cpp in Sources */,
+                               99CC0B5B18BE984A006CEBCC /* ReplaySessionSegment.cpp in Sources */,
                                F55B3DCF1251F12D003EF269 /* ResetInputType.cpp in Sources */,
                                514BC842161CF05C004D52F4 /* ResourceBuffer.cpp in Sources */,
                                514BC83F161CF04A004D52F4 /* ResourceBuffer.mm in Sources */,
                                BCD0E0FA0E972C3500265DEA /* SecurityOrigin.cpp in Sources */,
                                974D2DA4146A535D00D51F8B /* SecurityPolicy.cpp in Sources */,
                                371F4FFD0D25E7F300ECE0D5 /* SegmentedFontData.cpp in Sources */,
+                               99CC0B5D18BE984A006CEBCC /* SegmentedInputStorage.cpp in Sources */,
                                B2C3DA2E0D006C1D00EF6F26 /* SegmentedString.cpp in Sources */,
                                BEA807C80F714A0300524199 /* SelectionRect.cpp in Sources */,
                                E44B4BB3141650D7002B1D8B /* SelectorChecker.cpp in Sources */,
                                26B999961804D54200D01121 /* SelectorCompiler.cpp in Sources */,
                                415071571685067300C3C7B3 /* SelectorFilter.cpp in Sources */,
                                E45322AB140CE267005A0F92 /* SelectorQuery.cpp in Sources */,
+                               99CC0B5F18BE984A006CEBCC /* SerializationMethods.cpp in Sources */,
                                E18DF33818AAF14D00773E59 /* SerializedCryptoKeyWrapMac.mm in Sources */,
                                A75E497710752ACB00C9B896 /* SerializedScriptValue.cpp in Sources */,
                                93309E0F099E64920056E581 /* SetNodeAttributeCommand.cpp in Sources */,
                                B2227A230D00BF220071B782 /* SVGForeignObjectElement.cpp in Sources */,
                                B2227A260D00BF220071B782 /* SVGGElement.cpp in Sources */,
                                087E0AF613606D0B00FA4BA8 /* SVGGlyph.cpp in Sources */,
+                               99CC0B6A18BEA1FF006CEBCC /* WebReplayInputs.cpp in Sources */,
                                B2A1F2AD0CEF0ABF00442F6A /* SVGGlyphElement.cpp in Sources */,
                                24D912BD13CA9A9700D21915 /* SVGGlyphRefElement.cpp in Sources */,
                                B2227A290D00BF220071B782 /* SVGGradientElement.cpp in Sources */,
                                B2227AC00D00BF220071B782 /* SVGSymbolElement.cpp in Sources */,
                                B2227AC40D00BF220071B782 /* SVGTests.cpp in Sources */,
                                B2227AC70D00BF220071B782 /* SVGTextContentElement.cpp in Sources */,
-                               1AF4CEEC18BC3C1B00BC2D34 /* VisitedLinkStore.cpp in Sources */,
                                B2227ACA0D00BF220071B782 /* SVGTextElement.cpp in Sources */,
                                B2227ACD0D00BF220071B782 /* SVGTextPathElement.cpp in Sources */,
                                B2227AD00D00BF220071B782 /* SVGTextPositioningElement.cpp in Sources */,
                                B2227AEF0D00BF220071B782 /* SVGViewElement.cpp in Sources */,
                                B2227AF20D00BF220071B782 /* SVGViewSpec.cpp in Sources */,
                                8485228A1190173C006EDC7F /* SVGVKernElement.cpp in Sources */,
-                               1AF4CEE918BC350100BC2D34 /* DefaultVisitedLinkStore.cpp in Sources */,
                                B2227AF50D00BF220071B782 /* SVGZoomAndPan.cpp in Sources */,
                                B2E4EC970D00C22B00432643 /* SVGZoomEvent.cpp in Sources */,
                                E180811216FCF42F00B80D07 /* SynchronousLoaderClient.cpp in Sources */,
                                5DA97ECE168E787B000E3676 /* SystemVersionMac.mm in Sources */,
                                BCE3BEC20D222B1D007E06E4 /* TagNodeList.cpp in Sources */,
                                F55B3DD51251F12D003EF269 /* TelephoneInputType.cpp in Sources */,
+                               7CC564BA18BAC720001B9652 /* TelephoneNumberDetectorCocoa.cpp in Sources */,
                                7A29BA6A187B7C1D00F29CEB /* TemporaryOpenGLSetting.cpp in Sources */,
                                6550B6A5099DF0270090D781 /* Text.cpp in Sources */,
                                CE7B2DB61586ABAD0098B3FA /* TextAlternativeWithRange.mm in Sources */,
                                1AE79D42188DB61F002239C2 /* UserContentController.cpp in Sources */,
                                BCACF3BC1072921A00C0C8A3 /* UserContentURLPattern.cpp in Sources */,
                                2542F4DA1166C25A00E89A86 /* UserGestureIndicator.cpp in Sources */,
+                               9920398218B95BC600B39AF9 /* UserInputBridge.cpp in Sources */,
                                078E091217D14CEE00420AA1 /* UserMediaController.cpp in Sources */,
                                078E091317D14CEE00420AA1 /* UserMediaRequest.cpp in Sources */,
                                BCDF317B11F8D683003C5BF8 /* UserTypingGestureIndicator.cpp in Sources */,
                                A883DF270F3D045D00F19BF6 /* VisibleSelection.cpp in Sources */,
                                93309E1D099E64920056E581 /* VisibleUnits.cpp in Sources */,
                                419BC2DE1685329900D64D6D /* VisitedLinkState.cpp in Sources */,
+                               1AF4CEEC18BC3C1B00BC2D34 /* VisitedLinkStore.cpp in Sources */,
                                BE20507918A458680080647E /* VTTCue.cpp in Sources */,
                                A14832B1187F61E100DA63A6 /* WAKAppKitStubs.m in Sources */,
                                A14832B3187F629100DA63A6 /* WAKClipView.m in Sources */,
index e64df0b..55d94a4 100644 (file)
@@ -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
@@ -52,6 +53,7 @@
 #include "InspectorOverlay.h"
 #include "InspectorPageAgent.h"
 #include "InspectorProfilerAgent.h"
+#include "InspectorReplayAgent.h"
 #include "InspectorResourceAgent.h"
 #include "InspectorTimelineAgent.h"
 #include "InspectorWebBackendDispatchers.h"
@@ -124,6 +126,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));
index e7476e8..0eb63b8 100644 (file)
@@ -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
 #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 {
@@ -740,6 +746,13 @@ void InspectorInstrumentation::frameDetachedFromParentImpl(InstrumentingAgents*
 {
     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)
@@ -772,6 +785,10 @@ void InspectorInstrumentation::didCommitLoadImpl(InstrumentingAgents* instrument
         domAgent->didCommitLoad(loader->frame()->document());
     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)
@@ -1045,6 +1062,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())
@@ -1070,6 +1161,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())
index b5f63d1..5b3d8c8 100644 (file)
@@ -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
 #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&);
@@ -263,11 +290,13 @@ public:
     static bool hasFrontends() { return s_frontendCounter; }
     static bool consoleAgentEnabled(ScriptExecutionContext*);
     static bool timelineAgentEnabled(ScriptExecutionContext*);
+    static bool replayAgentEnabled(ScriptExecutionContext*);
 #else
     static bool hasFrontends() { 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*);
@@ -416,6 +445,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*);
@@ -1773,6 +1820,148 @@ inline void InspectorInstrumentation::didSendWebSocketFrame(Document* document,
 }
 #endif
 
+#if ENABLE(WEB_REPLAY)
+inline void InspectorInstrumentation::sessionCreated(Page* page, PassRefPtr<ReplaySession> session)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        segmentUnloadedImpl(instrumentingAgents);
+#else
+    UNUSED_PARAM(page);
+#endif
+}
+
+inline void InspectorInstrumentation::captureStarted(Page* page)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        captureStartedImpl(instrumentingAgents);
+#else
+    UNUSED_PARAM(page);
+#endif
+}
+
+inline void InspectorInstrumentation::captureStopped(Page* page)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        captureStoppedImpl(instrumentingAgents);
+#else
+    UNUSED_PARAM(page);
+#endif
+}
+
+inline void InspectorInstrumentation::playbackStarted(Page* page)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        playbackStartedImpl(instrumentingAgents);
+#else
+    UNUSED_PARAM(page);
+#endif
+}
+
+inline void InspectorInstrumentation::playbackPaused(Page* page, const ReplayPosition& position)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    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 (file)
index 0000000..9f45ca1
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * 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.release();
+}
+
+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_page(*pageAgent->page())
+{
+}
+
+InspectorReplayAgent::~InspectorReplayAgent()
+{
+    ASSERT(!m_sessionsMap.size());
+    ASSERT(!m_segmentsMap.size());
+}
+
+SessionState InspectorReplayAgent::sessionState() const
+{
+    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);
+    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.release());
+
+    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);
+
+    // 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;
+
+    auto result = m_sessionsMap.add(session->identifier(), session);
+    // Can't have two sessions with same identifier.
+    ASSERT_UNUSED(result, result.isNewEntry);
+
+    m_frontendDispatcher->sessionCreated(session->identifier());
+}
+
+void InspectorReplayAgent::sessionModified(PassRefPtr<ReplaySession> session)
+{
+    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);
+
+    m_frontendDispatcher->sessionLoaded(session->identifier());
+}
+
+void InspectorReplayAgent::segmentCreated(PassRefPtr<ReplaySessionSegment> prpSegment)
+{
+    RefPtr<ReplaySessionSegment> segment = prpSegment;
+
+    auto result = m_segmentsMap.add(segment->identifier(), segment);
+    // Can't have two segments with the same identifier.
+    ASSERT_UNUSED(result, result.isNewEntry);
+
+    m_frontendDispatcher->segmentCreated(segment->identifier());
+}
+
+void InspectorReplayAgent::segmentCompleted(PassRefPtr<ReplaySessionSegment> segment)
+{
+    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);
+
+    m_frontendDispatcher->segmentLoaded(segment->identifier());
+}
+
+void InspectorReplayAgent::segmentUnloaded()
+{
+    m_frontendDispatcher->segmentUnloaded();
+}
+
+void InspectorReplayAgent::captureStarted()
+{
+    LOG(WebReplay, "-----CAPTURE START-----");
+
+    m_frontendDispatcher->captureStarted();
+}
+
+void InspectorReplayAgent::captureStopped()
+{
+    LOG(WebReplay, "-----CAPTURE STOP-----");
+
+    m_frontendDispatcher->captureStopped();
+}
+
+void InspectorReplayAgent::playbackStarted()
+{
+    LOG(WebReplay, "-----REPLAY START-----");
+
+    m_frontendDispatcher->playbackStarted();
+}
+
+void InspectorReplayAgent::playbackPaused(const ReplayPosition& position)
+{
+    LOG(WebReplay, "-----REPLAY PAUSED-----");
+
+    m_frontendDispatcher->playbackPaused(buildInspectorObjectForPosition(position));
+}
+
+void InspectorReplayAgent::playbackHitPosition(const ReplayPosition& position)
+{
+    m_frontendDispatcher->playbackHitPosition(buildInspectorObjectForPosition(position), monotonicallyIncreasingTime());
+}
+
+void InspectorReplayAgent::startCapturing(ErrorString* errorString)
+{
+    if (sessionState() != SessionState::Inactive) {
+        *errorString = ASCIILiteral("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 = ASCIILiteral("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 = ASCIILiteral("Couldn't decode ReplayPosition segment offset provided to ReplayAgent.replayToPosition.");
+        return;
+    }
+
+    if (!positionObject->getNumber(ASCIILiteral("inputOffset"), &position.inputOffset)) {
+        *errorString = ASCIILiteral("Couldn't decode ReplayPosition input offset provided to ReplayAgent.replayToPosition.");
+        return;
+    }
+
+    if (sessionState() != SessionState::Inactive) {
+        *errorString = ASCIILiteral("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 = ASCIILiteral("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 = ASCIILiteral("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 = ASCIILiteral("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 = ASCIILiteral("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 = ASCIILiteral("Invalid segment index.");
+        return;
+    }
+
+    if (session == m_page.replayController().loadedSession() && sessionState() != SessionState::Inactive) {
+        *errorString = ASCIILiteral("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 = ASCIILiteral("Invalid segment index.");
+        return;
+    }
+
+    if (session == m_page.replayController().loadedSession() && sessionState() != SessionState::Inactive) {
+        *errorString = ASCIILiteral("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);
+
+    auto it = m_sessionsMap.find(identifier);
+    if (it == m_sessionsMap.end()) {
+        *errorString = ASCIILiteral("Couldn't find session with specified identifier");
+        return nullptr;
+    }
+
+    return it->value;
+}
+
+PassRefPtr<ReplaySessionSegment> InspectorReplayAgent::findSegment(ErrorString* errorString, SegmentIdentifier identifier)
+{
+    ASSERT(identifier > 0);
+
+    auto it = m_segmentsMap.find(identifier);
+    if (it == m_segmentsMap.end()) {
+        *errorString = ASCIILiteral("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& pair : m_sessionsMap)
+        sessionsList->addItem(pair.key);
+}
+
+void InspectorReplayAgent::getSerializedSession(ErrorString* errorString, SessionIdentifier identifier, RefPtr<Inspector::TypeBuilder::Replay::ReplaySession>& serializedObject)
+{
+    RefPtr<ReplaySession> session = findSession(errorString, identifier);
+    if (!session) {
+        *errorString = ASCIILiteral("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 = ASCIILiteral("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 (file)
index 0000000..c4f4cd8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.
+    virtual void startCapturing(ErrorString*) override;
+    virtual void stopCapturing(ErrorString*) override;
+
+    virtual void replayToPosition(ErrorString*, const RefPtr<Inspector::InspectorObject>&, bool shouldFastForward) override;
+    virtual void replayToCompletion(ErrorString*, bool shouldFastForward) override;
+    virtual void pausePlayback(ErrorString*) override;
+    virtual void cancelPlayback(ErrorString*) override;
+
+    virtual void switchSession(ErrorString*, SessionIdentifier) override;
+    virtual void insertSessionSegment(ErrorString*, SessionIdentifier, SegmentIdentifier, int segmentIndex) override;
+    virtual void removeSessionSegment(ErrorString*, SessionIdentifier, int segmentIndex) override;
+
+    virtual void getAvailableSessions(ErrorString*, RefPtr<Inspector::TypeBuilder::Array<SessionIdentifier>>&) override;
+    virtual void getSerializedSession(ErrorString*, SessionIdentifier, RefPtr<Inspector::TypeBuilder::Replay::ReplaySession>&) override;
+    virtual 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;
+    Page& m_page;
+
+    HashMap<int, RefPtr<ReplaySession>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> m_sessionsMap;
+    HashMap<int, RefPtr<ReplaySessionSegment>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> m_segmentsMap;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR) && ENABLE(WEB_REPLAY)
+
+#endif // InspectorReplayAgent_h
index 9cb4cf6..bd0a4ea 100644 (file)
@@ -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
@@ -81,6 +85,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
index 828cce0..3362e12 100644 (file)
@@ -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
@@ -55,6 +56,7 @@ class InspectorLayerTreeAgent;
 class InspectorPageAgent;
 class InspectorProfilerAgent;
 class InspectorResourceAgent;
+class InspectorReplayAgent;
 class InspectorTimelineAgent;
 class InspectorWorkerAgent;
 class Page;
@@ -107,6 +109,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; }
@@ -152,6 +159,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 (file)
index 0000000..6db6637
--- /dev/null
@@ -0,0 +1,242 @@
+{
+    "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 a segment within the currently-loaded replay session." },
+               { "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": "sessionIdentifier", "$ref": "SessionIdentifier" }
+            ],
+            "returns": [
+                { "name": "session", "$ref": "ReplaySession", "optional": true, "description": "The requested serialized replay session." }
+            ]
+        },
+        {
+            "name": "getSerializedSegment",
+            "description": "Returns the session segment serialized to a JSON object.",
+            "parameters": [
+                { "name": "id", "$ref": "SegmentIdentifier" }
+            ],
+            "returns": [
+                { "name": "segment", "$ref": "SessionSegment",  "optional": true, "description": "The requested serialized session segment." }
+            ]
+        }
+    ],
+    "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": "segmentIdentifier", "$ref": "SegmentIdentifier", "description": "Id for the loaded segment." }
+            ]
+        },
+        {
+            "name": "segmentUnloaded",
+            "description": "Fired when a segment is unloaded."
+        }
+    ]
+}
index fe2a727..790beef 100755 (executable)
@@ -3,6 +3,7 @@ export SOURCE_ROOT=$PWD
 export SRCROOT=$PWD
 export WebCore=$PWD
 export InspectorScripts=$PWD/../JavaScriptCore/inspector/scripts
+export WebReplayScripts=$PWD/../JavaScriptCore/replay/scripts
 
 mkdir -p DerivedSources/WebCore &&
 make -C DerivedSources/WebCore -f ../../DerivedSources.make $@
index 41e176a..8b8f683 100644 (file)
 #include <wtf/text/Base64.h>
 #include <wtf/text/StringHash.h>
 
+#if ENABLE(WEB_REPLAY)
+#include "ReplayController.h"
+#endif
+
 namespace WebCore {
 
 static HashSet<Page*>* allPages;
@@ -129,6 +133,9 @@ Page::Page(PageClients& pageClients)
     , m_contextMenuController(std::make_unique<ContextMenuController>(*this, *pageClients.contextMenuClient))
 #endif
     , m_userInputBridge(std::make_unique<UserInputBridge>(*this))
+#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
index 20f9bf1..284c1bb 100644 (file)
@@ -99,6 +99,7 @@ class ProgressTrackerClient;
 class Range;
 class RenderObject;
 class RenderTheme;
+class ReplayController;
 class VisibleSelection;
 class ScrollableArea;
 class ScrollingCoordinator;
@@ -199,6 +200,9 @@ public:
     ContextMenuController& contextMenuController() const { return *m_contextMenuController; }
 #endif
     UserInputBridge& userInputBridge() const { return *m_userInputBridge; }
+#if ENABLE(WEB_REPLAY)
+    ReplayController& replayController() const { return *m_replayController; }
+#endif
 #if ENABLE(INSPECTOR)
     InspectorController& inspectorController() const { return *m_inspectorController; }
 #endif
@@ -447,6 +451,9 @@ private:
     const std::unique_ptr<ContextMenuController> m_contextMenuController;
 #endif
     const std::unique_ptr<UserInputBridge> m_userInputBridge;
+#if ENABLE(WEB_REPLAY)
+    const std::unique_ptr<ReplayController> m_replayController;
+#endif
 #if ENABLE(INSPECTOR)
     const std::unique_ptr<InspectorController> m_inspectorController;
 #endif
index 4ef22f6..9062482 100644 (file)
@@ -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 (file)
index 0000000..7a951e2
--- /dev/null
@@ -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 (file)
index 0000000..253a5d1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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)
+{
+    LOG(WebReplay, "%-30sCreated capture cursor=%p.\n", "[ReplayController]", this);
+}
+
+CapturingInputCursor::~CapturingInputCursor()
+{
+    LOG(WebReplay, "%-30sDestroyed capture cursor=%p.\n", "[ReplayController]", 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(WEB_REPLAY)
diff --git a/Source/WebCore/replay/CapturingInputCursor.h b/Source/WebCore/replay/CapturingInputCursor.h
new file mode 100644 (file)
index 0000000..8f0df4d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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 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:
+    explicit CapturingInputCursor(SegmentedInputStorage&);
+
+    SegmentedInputStorage& m_storage;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
+
+#endif // CapturingInputCursor_h
index abb7d44..b4e3b70 100644 (file)
@@ -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 (file)
index 0000000..67e75a9
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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", &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", &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 as 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_running = false;
+        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 (file)
index 0000000..d7caae4
--- /dev/null
@@ -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 {
+    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 (file)
index 0000000..53c69c0
--- /dev/null
@@ -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 final : public InputCursor {
+    WTF_MAKE_NONCOPYABLE(FunctorInputCursor);
+public:
+    FunctorInputCursor(SegmentedInputStorage&);
+    virtual ~FunctorInputCursor() { }
+
+    // InputCursor
+    virtual bool isCapturing() const override { return false; }
+    virtual bool isReplaying() const override { return false; }
+
+    virtual void storeInput(std::unique_ptr<NondeterministicInputBase>) override;
+    virtual NondeterministicInputBase* uncheckedLoadInput(InputQueue) override;
+
+    template<typename Functor>
+    typename Functor::ReturnType forEachInputInQueue(InputQueue, Functor&);
+protected:
+    virtual NondeterministicInputBase* loadInput(InputQueue, const AtomicString&) override;
+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.queueSize(queue); i++)
+        functor(i, m_storage.queue(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 (file)
index 0000000..d299f2d
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * 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", m_loadedSession.get(), 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", 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;
+    bool shouldSuppressNotifications = true;
+    unloadSegment(shouldSuppressNotifications);
+
+    LOG(WebReplay, "%-20s Completed segment: %p.\n", "ReplayController", 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", 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", &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", 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::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());
+}
+
+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.
+}
+
+PassRefPtr<ReplaySession> ReplayController::loadedSession() const
+{
+    return m_loadedSession;
+}
+
+PassRefPtr<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 (file)
index 0000000..72368aa
--- /dev/null
@@ -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 DOMWindow;
+class Document;
+class DocumentLoader;
+class Element;
+class Event;
+class EventLoopInputBase;
+class Frame;
+class Node;
+class Page;
+class ReplaySession;
+class ReplaySessionSegment;
+
+// 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; }
+    PassRefPtr<ReplaySession> loadedSession() const;
+    PassRefPtr<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 (file)
index 0000000..0ed3e23
--- /dev/null
@@ -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 (file)
index 0000000..14f76ce
--- /dev/null
@@ -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)
index 8e8919e..98b14f1 100644 (file)
@@ -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);
 }
index df16ca0..b198423 100644 (file)
@@ -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 (file)
index 0000000..d448b19
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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_identifier(s_nextIdentifier++)
+    , m_timestamp(currentTimeMS())
+{
+}
+
+ReplaySession::~ReplaySession()
+{
+}
+
+PassRefPtr<ReplaySessionSegment> ReplaySession::at(size_t position) const
+{
+    return m_segments.at(position);
+}
+
+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.release());
+}
+
+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 (file)
index 0000000..fec81b4
--- /dev/null
@@ -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();
+
+    double timestamp() const { return m_timestamp; }
+    unsigned identifier() const { return m_identifier; }
+
+    size_t size() const { return m_segments.size(); }
+    PassRefPtr<ReplaySessionSegment> at(size_t position) const;
+
+    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 (file)
index 0000000..5a72be8
--- /dev/null
@@ -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(currentTimeMS())
+{
+}
+
+ReplaySessionSegment::~ReplaySessionSegment()
+{
+}
+
+PassRefPtr<CapturingInputCursor> ReplaySessionSegment::createCapturingCursor(Page&)
+{
+    ASSERT(m_canCapture);
+    m_canCapture = false;
+    return CapturingInputCursor::create(*m_storage);
+}
+
+PassRefPtr<ReplayingInputCursor> ReplaySessionSegment::createReplayingCursor(Page& page, EventLoopInputDispatcherClient* client)
+{
+    return ReplayingInputCursor::create(*m_storage, page, client);
+}
+
+std::unique_ptr<FunctorInputCursor> ReplaySessionSegment::createFunctorCursor()
+{
+    return std::make_unique<FunctorInputCursor>(*m_storage);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/ReplaySessionSegment.h b/Source/WebCore/replay/ReplaySessionSegment.h
new file mode 100644 (file)
index 0000000..1b16a39
--- /dev/null
@@ -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 (file)
index 0000000..a4f2db3
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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))
+{
+    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 (file)
index 0000000..14757ba
--- /dev/null
@@ -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 (file)
index 0000000..e66141a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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
+// These are used to make the log spew from LOG(WebReplay, ...) more readable.
+static const char* queueTypeToLogPrefix(InputQueue inputQueue, bool isLoad)
+{
+    if (isLoad) {
+        switch (inputQueue) {
+        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 (inputQueue) {
+        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 inputQueue)
+{
+    return static_cast<size_t>(inputQueue);
+}
+
+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 inputQueue, size_t offset)
+{
+    ASSERT(offset < queueSize(inputQueue));
+
+    NondeterministicInputBase* input = queue(inputQueue).at(offset).get();
+    ASSERT(input);
+
+    LOG(WebReplay, "%-20s %s: %s %s\n", "ReplayEvents", queueTypeToLogPrefix(inputQueue, 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 inputQueue) const
+{
+    return queue(inputQueue).size();
+}
+
+const SegmentedInputStorage::QueuedInputs& SegmentedInputStorage::queue(InputQueue queue) const
+{
+    ASSERT(queue < InputQueue::Count);
+    return *m_queues.at(offsetForInputQueue(queue));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_REPLAY)
diff --git a/Source/WebCore/replay/SegmentedInputStorage.h b/Source/WebCore/replay/SegmentedInputStorage.h
new file mode 100644 (file)
index 0000000..b87211b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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;
+    const QueuedInputs& queue(InputQueue) const;
+
+    Vector<QueuedInputs*, 3> 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 (file)
index 0000000..b86178b
--- /dev/null
@@ -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 (file)
index 0000000..8e112b8
--- /dev/null
@@ -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 (file)
index 0000000..5e651e1
--- /dev/null
@@ -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" }
+            ]
+        }
+    ]
+}