1/*
2 * Copyright (C) 2012 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "core/inspector/InspectorInputAgent.h"
33
34#include "core/frame/FrameView.h"
35#include "core/frame/LocalFrame.h"
36#include "core/inspector/InspectorClient.h"
37#include "core/page/Chrome.h"
38#include "core/page/EventHandler.h"
39#include "core/page/Page.h"
40#include "platform/JSONValues.h"
41#include "platform/PlatformKeyboardEvent.h"
42#include "platform/PlatformMouseEvent.h"
43#include "platform/PlatformTouchEvent.h"
44#include "platform/PlatformTouchPoint.h"
45#include "platform/geometry/FloatSize.h"
46#include "platform/geometry/IntPoint.h"
47#include "platform/geometry/IntRect.h"
48#include "platform/geometry/IntSize.h"
49#include "wtf/CurrentTime.h"
50
51namespace {
52
53class SyntheticInspectorTouchPoint : public blink::PlatformTouchPoint {
54public:
55    SyntheticInspectorTouchPoint(unsigned id, State state, const blink::IntPoint& screenPos, const blink::IntPoint& pos, int radiusX, int radiusY, double rotationAngle, double force)
56    {
57        m_id = id;
58        m_screenPos = screenPos;
59        m_pos = pos;
60        m_state = state;
61        m_radius = blink::FloatSize(radiusX, radiusY);
62        m_rotationAngle = rotationAngle;
63        m_force = force;
64    }
65};
66
67class SyntheticInspectorTouchEvent : public blink::PlatformTouchEvent {
68public:
69    SyntheticInspectorTouchEvent(const blink::PlatformEvent::Type type, unsigned modifiers, double timestamp)
70    {
71        m_type = type;
72        m_modifiers = modifiers;
73        m_timestamp = timestamp;
74    }
75
76    void append(const blink::PlatformTouchPoint& point)
77    {
78        m_touchPoints.append(point);
79    }
80};
81
82void ConvertInspectorPoint(blink::Page* page, const blink::IntPoint& point, blink::IntPoint* convertedPoint, blink::IntPoint* globalPoint)
83{
84    *convertedPoint = page->deprecatedLocalMainFrame()->view()->convertToContainingWindow(point);
85    *globalPoint = page->chrome().rootViewToScreen(blink::IntRect(point, blink::IntSize(0, 0))).location();
86}
87
88} // namespace
89
90namespace blink {
91
92InspectorInputAgent::InspectorInputAgent(Page* page, InspectorClient* client)
93    : InspectorBaseAgent<InspectorInputAgent>("Input")
94    , m_page(page), m_client(client)
95{
96}
97
98InspectorInputAgent::~InspectorInputAgent()
99{
100}
101
102void InspectorInputAgent::dispatchKeyEvent(ErrorString* error, const String& type, const int* modifiers, const double* timestamp, const String* text, const String* unmodifiedText, const String* keyIdentifier, const int* windowsVirtualKeyCode, const int* nativeVirtualKeyCode, const bool* autoRepeat, const bool* isKeypad, const bool* isSystemKey)
103{
104    PlatformEvent::Type convertedType;
105    if (type == "keyDown")
106        convertedType = PlatformEvent::KeyDown;
107    else if (type == "keyUp")
108        convertedType = PlatformEvent::KeyUp;
109    else if (type == "char")
110        convertedType = PlatformEvent::Char;
111    else if (type == "rawKeyDown")
112        convertedType = PlatformEvent::RawKeyDown;
113    else {
114        *error = "Unrecognized type: " + type;
115        return;
116    }
117
118    PlatformKeyboardEvent event(
119        convertedType,
120        text ? *text : "",
121        unmodifiedText ? *unmodifiedText : "",
122        keyIdentifier ? *keyIdentifier : "",
123        windowsVirtualKeyCode ? *windowsVirtualKeyCode : 0,
124        nativeVirtualKeyCode ? *nativeVirtualKeyCode : 0,
125        asBool(autoRepeat),
126        asBool(isKeypad),
127        asBool(isSystemKey),
128        static_cast<PlatformEvent::Modifiers>(modifiers ? *modifiers : 0),
129        timestamp ? *timestamp : currentTime());
130    m_client->dispatchKeyEvent(event);
131}
132
133void InspectorInputAgent::dispatchMouseEvent(ErrorString* error, const String& type, int x, int y, const int* modifiers, const double* timestamp, const String* button, const int* clickCount)
134{
135    PlatformEvent::Type convertedType;
136    if (type == "mousePressed")
137        convertedType = PlatformEvent::MousePressed;
138    else if (type == "mouseReleased")
139        convertedType = PlatformEvent::MouseReleased;
140    else if (type == "mouseMoved")
141        convertedType = PlatformEvent::MouseMoved;
142    else {
143        *error = "Unrecognized type: " + type;
144        return;
145    }
146
147    int convertedModifiers = modifiers ? *modifiers : 0;
148
149    MouseButton convertedButton = NoButton;
150    if (button) {
151        if (*button == "left")
152            convertedButton = LeftButton;
153        else if (*button == "middle")
154            convertedButton = MiddleButton;
155        else if (*button == "right")
156            convertedButton = RightButton;
157        else if (*button != "none") {
158            *error = "Unrecognized button: " + *button;
159            return;
160        }
161    }
162
163    // Some platforms may have flipped coordinate systems, but the given coordinates
164    // assume the origin is in the top-left of the window. Convert.
165    IntPoint convertedPoint, globalPoint;
166    ConvertInspectorPoint(m_page, IntPoint(x, y), &convertedPoint, &globalPoint);
167
168    PlatformMouseEvent event(
169        convertedPoint,
170        globalPoint,
171        convertedButton,
172        convertedType,
173        clickCount ? *clickCount : 0,
174        convertedModifiers & PlatformEvent::ShiftKey,
175        convertedModifiers & PlatformEvent::CtrlKey,
176        convertedModifiers & PlatformEvent::AltKey,
177        convertedModifiers & PlatformEvent::MetaKey,
178        PlatformMouseEvent::RealOrIndistinguishable,
179        timestamp ? *timestamp : currentTime());
180
181    m_client->dispatchMouseEvent(event);
182}
183
184void InspectorInputAgent::dispatchTouchEvent(ErrorString* error, const String& type, const RefPtr<JSONArray>& touchPoints, const int* modifiers, const double* timestamp)
185{
186    PlatformEvent::Type convertedType;
187    if (type == "touchStart") {
188        convertedType = PlatformEvent::TouchStart;
189    } else if (type == "touchEnd") {
190        convertedType = PlatformEvent::TouchEnd;
191    } else if (type == "touchMove") {
192        convertedType = PlatformEvent::TouchMove;
193    } else {
194        *error = "Unrecognized type: " + type;
195        return;
196    }
197
198    unsigned convertedModifiers = modifiers ? *modifiers : 0;
199
200    SyntheticInspectorTouchEvent event(convertedType, convertedModifiers, timestamp ? *timestamp : currentTime());
201
202    int autoId = 0;
203    JSONArrayBase::iterator iter;
204    for (iter = touchPoints->begin(); iter != touchPoints->end(); ++iter) {
205        RefPtr<JSONObject> pointObj;
206        String state;
207        int x, y, radiusX, radiusY, id;
208        double rotationAngle, force;
209        (*iter)->asObject(&pointObj);
210        if (!pointObj->getString("state", &state)) {
211            *error = "TouchPoint missing 'state'";
212            return;
213        }
214        if (!pointObj->getNumber("x", &x)) {
215            *error = "TouchPoint missing 'x' coordinate";
216            return;
217        }
218        if (!pointObj->getNumber("y", &y)) {
219            *error = "TouchPoint missing 'y' coordinate";
220            return;
221        }
222        if (!pointObj->getNumber("radiusX", &radiusX))
223            radiusX = 1;
224        if (!pointObj->getNumber("radiusY", &radiusY))
225            radiusY = 1;
226        if (!pointObj->getNumber("rotationAngle", &rotationAngle))
227            rotationAngle = 0.0f;
228        if (!pointObj->getNumber("force", &force))
229            force = 1.0f;
230        if (pointObj->getNumber("id", &id)) {
231            if (autoId > 0)
232                id = -1;
233            autoId = -1;
234        } else {
235            id = autoId++;
236        }
237        if (id < 0) {
238            *error = "All or none of the provided TouchPoints must supply positive integer ids.";
239            return;
240        }
241
242        PlatformTouchPoint::State convertedState;
243        if (state == "touchPressed") {
244            convertedState = PlatformTouchPoint::TouchPressed;
245        } else if (state == "touchReleased") {
246            convertedState = PlatformTouchPoint::TouchReleased;
247        } else if (state == "touchMoved") {
248            convertedState = PlatformTouchPoint::TouchMoved;
249        } else if (state == "touchStationary") {
250            convertedState = PlatformTouchPoint::TouchStationary;
251        } else if (state == "touchCancelled") {
252            convertedState = PlatformTouchPoint::TouchCancelled;
253        } else {
254            *error = "Unrecognized state: " + state;
255            return;
256        }
257
258        // Some platforms may have flipped coordinate systems, but the given coordinates
259        // assume the origin is in the top-left of the window. Convert.
260        IntPoint convertedPoint, globalPoint;
261        ConvertInspectorPoint(m_page, IntPoint(x, y), &convertedPoint, &globalPoint);
262
263        SyntheticInspectorTouchPoint point(id++, convertedState, globalPoint, convertedPoint, radiusX, radiusY, rotationAngle, force);
264        event.append(point);
265    }
266
267    m_page->deprecatedLocalMainFrame()->eventHandler().handleTouchEvent(event);
268}
269
270void InspectorInputAgent::trace(Visitor* visitor)
271{
272    visitor->trace(m_page);
273    InspectorBaseAgent::trace(visitor);
274}
275
276} // namespace blink
277
278