1/*
2 * Copyright (C) 2011-2013 University of Washington. All rights reserved.
3 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifndef ReplayController_h
29#define ReplayController_h
30
31#if ENABLE(WEB_REPLAY)
32
33#include "EventLoopInputDispatcher.h"
34#include <wtf/Noncopyable.h>
35#include <wtf/Vector.h>
36
37// Determinism assertions are guarded by this macro. When a user-facing error reporting and
38// recovery mechanism is implemented, this guard can be removed. <https://webkit.org/b/131279>
39#define ENABLE_AGGRESSIVE_DETERMINISM_CHECKS 0
40
41namespace JSC {
42class InputCursor;
43}
44
45namespace WebCore {
46
47class DOMWindow;
48class Document;
49class DocumentLoader;
50class Element;
51class Event;
52class EventLoopInputBase;
53class Frame;
54class Node;
55class Page;
56class ReplaySession;
57class ReplaySessionSegment;
58
59// Each state may transition to the state immediately above or below it.
60// SessionState transitions are only allowed when SegmentState is Unloaded.
61enum class SessionState {
62    Capturing,
63    // Neither capturing or replaying. m_currentPosition is not valid in this state.
64    Inactive,
65    Replaying,
66};
67
68// Each state may transition to the state immediately above or below it.
69enum class SegmentState {
70    // Inputs can be appended into an unassociated session segment.
71    // We can stop capturing, which reverts to the Unloaded state.
72    Appending,
73    // No session segment is loaded.
74    // We can start capturing, or load a segment (and then replay it).
75    Unloaded,
76    // A session segment is loaded.
77    // We can unload the segment, or begin playback from m_currentPosition.
78    Loaded,
79    // The controller is actively dispatching event loop inputs.
80    // We can pause or cancel playback, which reverts to the Loaded state.
81    Dispatching,
82};
83
84struct ReplayPosition {
85    ReplayPosition(unsigned segmentOffset, unsigned inputOffset)
86        : segmentOffset(segmentOffset)
87        , inputOffset(inputOffset)
88    {
89    }
90
91    // By convention, this position represents the end of the last segment of the session.
92    ReplayPosition()
93        : segmentOffset(0)
94        , inputOffset(0)
95    {
96    }
97
98    bool operator<(const ReplayPosition& other)
99    {
100        return segmentOffset <= other.segmentOffset && inputOffset < other.inputOffset;
101    }
102
103    bool operator==(const ReplayPosition& other)
104    {
105        return segmentOffset == other.segmentOffset && inputOffset == other.inputOffset;
106    }
107
108    unsigned segmentOffset;
109    unsigned inputOffset;
110};
111
112class ReplayController final : public EventLoopInputDispatcherClient {
113    WTF_MAKE_NONCOPYABLE(ReplayController);
114public:
115    ReplayController(Page&);
116
117    void startCapturing();
118    void stopCapturing();
119
120    // Start or resume playback with default speed and target replay position.
121    void startPlayback();
122    void pausePlayback();
123    void cancelPlayback();
124
125    void replayToPosition(const ReplayPosition&, DispatchSpeed = DispatchSpeed::FastForward);
126    void replayToCompletion(DispatchSpeed speed = DispatchSpeed::FastForward)
127    {
128        replayToPosition(ReplayPosition(), speed);
129    }
130
131    void switchSession(PassRefPtr<ReplaySession>);
132
133    // InspectorReplayAgent notifications.
134    void frameNavigated(DocumentLoader*);
135    void frameDetached(Frame*);
136    void willDispatchEvent(const Event&, Frame*);
137
138    Page& page() const { return m_page; }
139    SessionState sessionState() const { return m_sessionState; }
140    PassRefPtr<ReplaySession> loadedSession() const;
141    PassRefPtr<ReplaySessionSegment> loadedSegment() const;
142    JSC::InputCursor& activeInputCursor() const;
143
144private:
145    // EventLoopInputDispatcherClient API
146    virtual void willDispatchInput(const EventLoopInputBase&) override;
147    virtual void didDispatchInput(const EventLoopInputBase&) override;
148    virtual void didDispatchFinalInput() override;
149
150    void createSegment();
151    void completeSegment();
152
153    void loadSegmentAtIndex(size_t);
154    void unloadSegment(bool suppressNotifications = false);
155
156    EventLoopInputDispatcher& dispatcher() const;
157
158    void setSessionState(SessionState);
159    void setForceDeterministicSettings(bool);
160
161    struct SavedSettings {
162        bool usesPageCache;
163
164        SavedSettings()
165            : usesPageCache(false)
166        { }
167    };
168
169    Page& m_page;
170
171    RefPtr<ReplaySessionSegment> m_loadedSegment;
172    RefPtr<ReplaySession> m_loadedSession;
173    const RefPtr<JSC::InputCursor> m_emptyCursor;
174    // The active cursor is set to nullptr when invalid.
175    RefPtr<JSC::InputCursor> m_activeCursor;
176
177    // This position is valid when SessionState == Replaying.
178    ReplayPosition m_targetPosition;
179    // This position is valid when SessionState != Inactive.
180    ReplayPosition m_currentPosition;
181    SegmentState m_segmentState;
182    // This tracks state across multiple segments. When navigating the main frame,
183    // there is a small interval during segment switching when no segment is loaded.
184    SessionState m_sessionState;
185
186    DispatchSpeed m_dispatchSpeed;
187    SavedSettings m_savedSettings;
188};
189
190} // namespace WebCore
191
192#endif // ENABLE(WEB_REPLAY)
193
194#endif // ReplayController_h
195