1/*
2 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
3 * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de)
4 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
5 * Copyright (C) 2003, 2005, 2006, 2008, 2013 Apple Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24#include "MouseEvent.h"
25
26#include "DataTransfer.h"
27#include "EventNames.h"
28#include "Frame.h"
29#include "FrameView.h"
30#include "HTMLIFrameElement.h"
31#include "PlatformMouseEvent.h"
32#include <wtf/CurrentTime.h>
33
34namespace WebCore {
35
36MouseEventInit::MouseEventInit()
37    : screenX(0)
38    , screenY(0)
39    , clientX(0)
40    , clientY(0)
41    , ctrlKey(false)
42    , altKey(false)
43    , shiftKey(false)
44    , metaKey(false)
45    , button(0)
46    , relatedTarget(0)
47{
48}
49
50PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, const MouseEventInit& initializer)
51{
52    return adoptRef(new MouseEvent(type, initializer));
53}
54
55PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, const PlatformMouseEvent& event, int detail, PassRefPtr<Node> relatedTarget)
56{
57    ASSERT(event.type() == PlatformEvent::MouseMoved || event.button() != NoButton);
58
59    bool isMouseEnterOrLeave = eventType == eventNames().mouseenterEvent || eventType == eventNames().mouseleaveEvent;
60    bool isCancelable = eventType != eventNames().mousemoveEvent && !isMouseEnterOrLeave;
61    bool canBubble = !isMouseEnterOrLeave;
62
63    return MouseEvent::create(eventType, canBubble, isCancelable, event.timestamp(), view,
64        detail, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
65#if ENABLE(POINTER_LOCK)
66        event.movementDelta().x(), event.movementDelta().y(),
67#endif
68        event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), event.button(),
69        relatedTarget);
70}
71
72PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view,
73    int detail, int screenX, int screenY, int pageX, int pageY,
74#if ENABLE(POINTER_LOCK)
75    int movementX, int movementY,
76#endif
77    bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button,
78    PassRefPtr<EventTarget> relatedTarget)
79
80{
81    return MouseEvent::create(type, canBubble, cancelable, timestamp, view,
82        detail, screenX, screenY, pageX, pageY,
83#if ENABLE(POINTER_LOCK)
84        movementX, movementY,
85#endif
86        ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, 0, false);
87}
88
89PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view,
90    int detail, int screenX, int screenY, int pageX, int pageY,
91#if ENABLE(POINTER_LOCK)
92    int movementX, int movementY,
93#endif
94    bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button,
95    PassRefPtr<EventTarget> relatedTarget, PassRefPtr<DataTransfer> dataTransfer, bool isSimulated)
96{
97    return adoptRef(new MouseEvent(type, canBubble, cancelable, timestamp, view,
98        detail, screenX, screenY, pageX, pageY,
99#if ENABLE(POINTER_LOCK)
100        movementX, movementY,
101#endif
102        ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, dataTransfer, isSimulated));
103}
104
105MouseEvent::MouseEvent()
106    : m_button(0)
107    , m_buttonDown(false)
108{
109}
110
111MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view,
112                       int detail, int screenX, int screenY, int pageX, int pageY,
113#if ENABLE(POINTER_LOCK)
114                       int movementX, int movementY,
115#endif
116                       bool ctrlKey, bool altKey, bool shiftKey, bool metaKey,
117                       unsigned short button, PassRefPtr<EventTarget> relatedTarget,
118                       PassRefPtr<DataTransfer> dataTransfer, bool isSimulated)
119    : MouseRelatedEvent(eventType, canBubble, cancelable, timestamp, view, detail, IntPoint(screenX, screenY),
120                        IntPoint(pageX, pageY),
121#if ENABLE(POINTER_LOCK)
122                        IntPoint(movementX, movementY),
123#endif
124                        ctrlKey, altKey, shiftKey, metaKey, isSimulated)
125    , m_button(button == (unsigned short)-1 ? 0 : button)
126    , m_buttonDown(button != (unsigned short)-1)
127    , m_relatedTarget(relatedTarget)
128    , m_dataTransfer(dataTransfer)
129{
130}
131
132MouseEvent::MouseEvent(const AtomicString& eventType, const MouseEventInit& initializer)
133    : MouseRelatedEvent(eventType, initializer.bubbles, initializer.cancelable, currentTime(), initializer.view, initializer.detail, IntPoint(initializer.screenX, initializer.screenY),
134        IntPoint(0 /* pageX */, 0 /* pageY */),
135#if ENABLE(POINTER_LOCK)
136        IntPoint(0 /* movementX */, 0 /* movementY */),
137#endif
138        initializer.ctrlKey, initializer.altKey, initializer.shiftKey, initializer.metaKey, false /* isSimulated */)
139    , m_button(initializer.button == (unsigned short)-1 ? 0 : initializer.button)
140    , m_buttonDown(initializer.button != (unsigned short)-1)
141    , m_relatedTarget(initializer.relatedTarget)
142    , m_dataTransfer(0 /* dataTransfer */)
143{
144    initCoordinates(IntPoint(initializer.clientX, initializer.clientY));
145}
146
147MouseEvent::~MouseEvent()
148{
149}
150
151void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view,
152                                int detail, int screenX, int screenY, int clientX, int clientY,
153                                bool ctrlKey, bool altKey, bool shiftKey, bool metaKey,
154                                unsigned short button, PassRefPtr<EventTarget> relatedTarget)
155{
156    if (dispatched())
157        return;
158
159    initUIEvent(type, canBubble, cancelable, view, detail);
160
161    m_screenLocation = IntPoint(screenX, screenY);
162    m_ctrlKey = ctrlKey;
163    m_altKey = altKey;
164    m_shiftKey = shiftKey;
165    m_metaKey = metaKey;
166    m_button = button == (unsigned short)-1 ? 0 : button;
167    m_buttonDown = button != (unsigned short)-1;
168    m_relatedTarget = relatedTarget;
169
170    initCoordinates(IntPoint(clientX, clientY));
171
172    // FIXME: m_isSimulated is not set to false here.
173    // FIXME: m_dataTransfer is not set to 0 here.
174}
175
176EventInterface MouseEvent::eventInterface() const
177{
178    return MouseEventInterfaceType;
179}
180
181bool MouseEvent::isMouseEvent() const
182{
183    return true;
184}
185
186bool MouseEvent::isDragEvent() const
187{
188    const AtomicString& t = type();
189    return t == eventNames().dragenterEvent || t == eventNames().dragoverEvent || t == eventNames().dragleaveEvent || t == eventNames().dropEvent
190               || t == eventNames().dragstartEvent|| t == eventNames().dragEvent || t == eventNames().dragendEvent;
191}
192
193int MouseEvent::which() const
194{
195    // For the DOM, the return values for left, middle and right mouse buttons are 0, 1, 2, respectively.
196    // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively.
197    // So we must add 1.
198    if (!m_buttonDown)
199        return 0;
200    return m_button + 1;
201}
202
203Node* MouseEvent::toElement() const
204{
205    // MSIE extension - "the object toward which the user is moving the mouse pointer"
206    if (type() == eventNames().mouseoutEvent || type() == eventNames().mouseleaveEvent) {
207        EventTarget* relatedTarget = this->relatedTarget();
208        return relatedTarget ? relatedTarget->toNode() : nullptr;
209    }
210
211    return target() ? target()->toNode() : nullptr;
212}
213
214Node* MouseEvent::fromElement() const
215{
216    // MSIE extension - "object from which activation or the mouse pointer is exiting during the event" (huh?)
217    if (type() != eventNames().mouseoutEvent && type() != eventNames().mouseleaveEvent) {
218        EventTarget* relatedTarget = this->relatedTarget();
219        return relatedTarget ? relatedTarget->toNode() : nullptr;
220    }
221
222    return target() ? target()->toNode() : nullptr;
223}
224
225// FIXME: Fix positioning. e.g. We need to consider border/padding.
226// https://bugs.webkit.org/show_bug.cgi?id=93696
227inline static int adjustedClientX(int innerClientX, HTMLIFrameElement* iframe, FrameView* frameView)
228{
229    return iframe->offsetLeft() - frameView->scrollX() + innerClientX;
230}
231
232inline static int adjustedClientY(int innerClientY, HTMLIFrameElement* iframe, FrameView* frameView)
233{
234    return iframe->offsetTop() - frameView->scrollY() + innerClientY;
235}
236
237PassRefPtr<Event> MouseEvent::cloneFor(HTMLIFrameElement* iframe) const
238{
239    ASSERT(iframe);
240    RefPtr<MouseEvent> clonedMouseEvent = MouseEvent::create();
241    Frame* frame = iframe->document().frame();
242    FrameView* frameView = frame ? frame->view() : 0;
243    clonedMouseEvent->initMouseEvent(type(), bubbles(), cancelable(),
244        iframe->document().defaultView(),
245        detail(), screenX(), screenY(),
246        frameView ? adjustedClientX(clientX(), iframe, frameView) : 0,
247        frameView ? adjustedClientY(clientY(), iframe, frameView) : 0,
248        ctrlKey(), altKey(), shiftKey(), metaKey(),
249        button(),
250        // Nullifies relatedTarget.
251        0);
252    return clonedMouseEvent.release();
253}
254
255PassRefPtr<SimulatedMouseEvent> SimulatedMouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent, Element* target)
256{
257    return adoptRef(new SimulatedMouseEvent(eventType, view, underlyingEvent, target));
258}
259
260SimulatedMouseEvent::~SimulatedMouseEvent()
261{
262}
263
264SimulatedMouseEvent::SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent, Element* target)
265    : MouseEvent(eventType, true, true, underlyingEvent ? underlyingEvent->timeStamp() : currentTime(), view, 0, 0, 0, 0, 0,
266#if ENABLE(POINTER_LOCK)
267                 0, 0,
268#endif
269                 false, false, false, false, 0, 0, 0, true)
270{
271    if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) {
272        m_ctrlKey = keyStateEvent->ctrlKey();
273        m_altKey = keyStateEvent->altKey();
274        m_shiftKey = keyStateEvent->shiftKey();
275        m_metaKey = keyStateEvent->metaKey();
276    }
277    setUnderlyingEvent(underlyingEvent);
278
279    if (this->underlyingEvent() && this->underlyingEvent()->isMouseEvent()) {
280        MouseEvent* mouseEvent = toMouseEvent(this->underlyingEvent());
281        m_screenLocation = mouseEvent->screenLocation();
282        initCoordinates(mouseEvent->clientLocation());
283    } else if (target) {
284        m_screenLocation = target->screenRect().center();
285        initCoordinates(LayoutPoint(target->clientRect().center()));
286    }
287}
288
289} // namespace WebCore
290