1/*
2 * Copyright (C) 2018, 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#if ENABLE(WEBDRIVER_ACTIONS_API)
29
30#include "WebEvent.h"
31#include <wtf/CompletionHandler.h>
32#include <wtf/HashSet.h>
33#include <wtf/Optional.h>
34#include <wtf/RefCounted.h>
35#include <wtf/RunLoop.h>
36#include <wtf/Seconds.h>
37#include <wtf/Vector.h>
38#include <wtf/text/WTFString.h>
39
40namespace Inspector { namespace Protocol { namespace Automation {
41enum class ErrorMessage;
42enum class KeyboardInteractionType;
43enum class MouseInteraction;
44enum class MouseMoveOrigin;
45enum class VirtualKey;
46} } }
47
48namespace WebKit {
49
50class AutomationCommandError;
51using AutomationCompletionHandler = WTF::CompletionHandler<void(Optional<AutomationCommandError>)>;
52
53class WebPageProxy;
54
55using KeyboardInteraction = Inspector::Protocol::Automation::KeyboardInteractionType;
56using VirtualKey = Inspector::Protocol::Automation::VirtualKey;
57using VirtualKeySet = HashSet<VirtualKey, WTF::IntHash<VirtualKey>, WTF::StrongEnumHashTraits<VirtualKey>>;
58using CharKey = char; // For WebDriver, this only needs to support ASCII characters on 102-key keyboard.
59using MouseButton = WebMouseEvent::Button;
60using MouseInteraction = Inspector::Protocol::Automation::MouseInteraction;
61using MouseMoveOrigin = Inspector::Protocol::Automation::MouseMoveOrigin;
62
63enum class SimulatedInputSourceType {
64 Null, // Used to induce a minimum duration.
65 Keyboard,
66 Mouse,
67 Touch,
68};
69
70enum class TouchInteraction {
71 TouchDown,
72 MoveTo,
73 LiftUp,
74};
75
76struct SimulatedInputSourceState {
77 Optional<CharKey> pressedCharKey;
78 VirtualKeySet pressedVirtualKeys;
79 Optional<MouseButton> pressedMouseButton;
80 Optional<MouseMoveOrigin> origin;
81 Optional<String> nodeHandle;
82 Optional<WebCore::IntPoint> location;
83 Optional<Seconds> duration;
84
85 static SimulatedInputSourceState emptyStateForSourceType(SimulatedInputSourceType);
86};
87
88struct SimulatedInputSource : public RefCounted<SimulatedInputSource> {
89public:
90 SimulatedInputSourceType type;
91
92 // The last state associated with this input source.
93 SimulatedInputSourceState state;
94
95 static Ref<SimulatedInputSource> create(SimulatedInputSourceType type)
96 {
97 return adoptRef(*new SimulatedInputSource(type));
98 }
99
100private:
101 SimulatedInputSource(SimulatedInputSourceType type)
102 : type(type)
103 , state(SimulatedInputSourceState::emptyStateForSourceType(type))
104 { }
105};
106
107struct SimulatedInputKeyFrame {
108public:
109 using StateEntry = std::pair<SimulatedInputSource&, SimulatedInputSourceState>;
110
111 explicit SimulatedInputKeyFrame(Vector<StateEntry>&&);
112 Seconds maximumDuration() const;
113
114 static SimulatedInputKeyFrame keyFrameFromStateOfInputSources(HashSet<Ref<SimulatedInputSource>>&);
115 static SimulatedInputKeyFrame keyFrameToResetInputSources(HashSet<Ref<SimulatedInputSource>>&);
116
117 Vector<StateEntry> states;
118};
119
120class SimulatedInputDispatcher : public RefCounted<SimulatedInputDispatcher> {
121 WTF_MAKE_NONCOPYABLE(SimulatedInputDispatcher);
122public:
123 class Client {
124 public:
125 virtual ~Client() { }
126#if ENABLE(WEBDRIVER_MOUSE_INTERACTIONS)
127 virtual void simulateMouseInteraction(WebPageProxy&, MouseInteraction, WebMouseEvent::Button, const WebCore::IntPoint& locationInView, AutomationCompletionHandler&&) = 0;
128#endif
129#if ENABLE(WEBDRIVER_TOUCH_INTERACTIONS)
130 virtual void simulateTouchInteraction(WebPageProxy&, TouchInteraction, const WebCore::IntPoint& locationInView, Optional<Seconds> duration, AutomationCompletionHandler&&) = 0;
131#endif
132#if ENABLE(WEBDRIVER_KEYBOARD_INTERACTIONS)
133 virtual void simulateKeyboardInteraction(WebPageProxy&, KeyboardInteraction, WTF::Variant<VirtualKey, CharKey>&&, AutomationCompletionHandler&&) = 0;
134#endif
135 virtual void viewportInViewCenterPointOfElement(WebPageProxy&, uint64_t frameID, const String& nodeHandle, Function<void (Optional<WebCore::IntPoint>, Optional<AutomationCommandError>)>&&) = 0;
136 };
137
138 static Ref<SimulatedInputDispatcher> create(WebPageProxy& page, SimulatedInputDispatcher::Client& client)
139 {
140 return adoptRef(*new SimulatedInputDispatcher(page, client));
141 }
142
143 ~SimulatedInputDispatcher();
144
145 void run(uint64_t frameID, Vector<SimulatedInputKeyFrame>&& keyFrames, HashSet<Ref<SimulatedInputSource>>& inputSources, AutomationCompletionHandler&&);
146 void cancel();
147
148 bool isActive() const;
149
150private:
151 SimulatedInputDispatcher(WebPageProxy&, SimulatedInputDispatcher::Client&);
152
153 void transitionToNextKeyFrame();
154 void transitionBetweenKeyFrames(const SimulatedInputKeyFrame&, const SimulatedInputKeyFrame&, AutomationCompletionHandler&&);
155
156 void transitionToNextInputSourceState();
157 void transitionInputSourceToState(SimulatedInputSource&, SimulatedInputSourceState& newState, AutomationCompletionHandler&&);
158 void finishDispatching(Optional<AutomationCommandError>);
159
160 void keyFrameTransitionDurationTimerFired();
161 bool isKeyFrameTransitionComplete() const;
162
163 void resolveLocation(const WebCore::IntPoint& currentLocation, Optional<WebCore::IntPoint> location, MouseMoveOrigin, Optional<String> nodeHandle, Function<void (Optional<WebCore::IntPoint>, Optional<AutomationCommandError>)>&&);
164
165 WebPageProxy& m_page;
166 SimulatedInputDispatcher::Client& m_client;
167
168 Optional<uint64_t> m_frameID;
169 AutomationCompletionHandler m_runCompletionHandler;
170 AutomationCompletionHandler m_keyFrameTransitionCompletionHandler;
171 RunLoop::Timer<SimulatedInputDispatcher> m_keyFrameTransitionDurationTimer;
172
173 Vector<SimulatedInputKeyFrame> m_keyframes;
174 HashSet<Ref<SimulatedInputSource>> m_inputSources;
175
176 // The position within m_keyframes.
177 unsigned m_keyframeIndex { 0 };
178
179 // The position within the input source state vector at m_keyframes[m_keyframeIndex].
180 // Events that reflect input source state transitions are dispatched serially based on this order.
181 unsigned m_inputSourceStateIndex { 0 };
182};
183
184} // namespace WebKit
185
186#endif // ENABLE(WEBDRIVER_ACTIONS_API)
187