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 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 "Clipboard.h"
27#include "EventDispatcher.h"
28#include "EventNames.h"
29#include "EventRetargeter.h"
30#include "Frame.h"
31#include "FrameView.h"
32#include "HTMLIFrameElement.h"
33#include "PlatformMouseEvent.h"
34
35namespace WebCore {
36
37MouseEventInit::MouseEventInit()
38    : screenX(0)
39    , screenY(0)
40    , clientX(0)
41    , clientY(0)
42    , ctrlKey(false)
43    , altKey(false)
44    , shiftKey(false)
45    , metaKey(false)
46    , button(0)
47    , relatedTarget(0)
48{
49}
50
51PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, const MouseEventInit& initializer)
52{
53    return adoptRef(new MouseEvent(type, initializer));
54}
55
56PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, const PlatformMouseEvent& event, int detail, PassRefPtr<Node> relatedTarget)
57{
58    ASSERT(event.type() == PlatformEvent::MouseMoved || event.button() != NoButton);
59
60    bool isMouseEnterOrLeave = eventType == eventNames().mouseenterEvent || eventType == eventNames().mouseleaveEvent;
61    bool isCancelable = eventType != eventNames().mousemoveEvent && !isMouseEnterOrLeave;
62    bool canBubble = !isMouseEnterOrLeave;
63
64    return MouseEvent::create(eventType, canBubble, isCancelable, view,
65        detail, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
66#if ENABLE(POINTER_LOCK)
67        event.movementDelta().x(), event.movementDelta().y(),
68#endif
69        event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), event.button(),
70        relatedTarget, 0, false);
71}
72
73PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view,
74    int detail, int screenX, int screenY, int pageX, int pageY,
75#if ENABLE(POINTER_LOCK)
76    int movementX, int movementY,
77#endif
78    bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button,
79    PassRefPtr<EventTarget> relatedTarget)
80
81{
82    return MouseEvent::create(type, canBubble, cancelable, view,
83        detail, screenX, screenY, pageX, pageY,
84#if ENABLE(POINTER_LOCK)
85        movementX, movementY,
86#endif
87        ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, 0, false);
88}
89
90PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view,
91    int detail, int screenX, int screenY, int pageX, int pageY,
92#if ENABLE(POINTER_LOCK)
93    int movementX, int movementY,
94#endif
95    bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button,
96    PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard> clipboard, bool isSimulated)
97{
98    return adoptRef(new MouseEvent(type, canBubble, cancelable, view,
99        detail, screenX, screenY, pageX, pageY,
100#if ENABLE(POINTER_LOCK)
101        movementX, movementY,
102#endif
103        ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, clipboard, isSimulated));
104}
105
106MouseEvent::MouseEvent()
107    : m_button(0)
108    , m_buttonDown(false)
109{
110}
111
112MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view,
113                       int detail, int screenX, int screenY, int pageX, int pageY,
114#if ENABLE(POINTER_LOCK)
115                       int movementX, int movementY,
116#endif
117                       bool ctrlKey, bool altKey, bool shiftKey, bool metaKey,
118                       unsigned short button, PassRefPtr<EventTarget> relatedTarget,
119                       PassRefPtr<Clipboard> clipboard, bool isSimulated)
120    : MouseRelatedEvent(eventType, canBubble, cancelable, view, detail, IntPoint(screenX, screenY),
121                        IntPoint(pageX, pageY),
122#if ENABLE(POINTER_LOCK)
123                        IntPoint(movementX, movementY),
124#endif
125                        ctrlKey, altKey, shiftKey, metaKey, isSimulated)
126    , m_button(button == (unsigned short)-1 ? 0 : button)
127    , m_buttonDown(button != (unsigned short)-1)
128    , m_relatedTarget(relatedTarget)
129    , m_clipboard(clipboard)
130{
131}
132
133MouseEvent::MouseEvent(const AtomicString& eventType, const MouseEventInit& initializer)
134    : MouseRelatedEvent(eventType, initializer.bubbles, initializer.cancelable, initializer.view, initializer.detail, IntPoint(initializer.screenX, initializer.screenY),
135        IntPoint(0 /* pageX */, 0 /* pageY */),
136#if ENABLE(POINTER_LOCK)
137        IntPoint(0 /* movementX */, 0 /* movementY */),
138#endif
139        initializer.ctrlKey, initializer.altKey, initializer.shiftKey, initializer.metaKey, false /* isSimulated */)
140    , m_button(initializer.button == (unsigned short)-1 ? 0 : initializer.button)
141    , m_buttonDown(initializer.button != (unsigned short)-1)
142    , m_relatedTarget(initializer.relatedTarget)
143    , m_clipboard(0 /* clipboard */)
144{
145    initCoordinates(IntPoint(initializer.clientX, initializer.clientY));
146}
147
148MouseEvent::~MouseEvent()
149{
150}
151
152void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view,
153                                int detail, int screenX, int screenY, int clientX, int clientY,
154                                bool ctrlKey, bool altKey, bool shiftKey, bool metaKey,
155                                unsigned short button, PassRefPtr<EventTarget> relatedTarget)
156{
157    if (dispatched())
158        return;
159
160    initUIEvent(type, canBubble, cancelable, view, detail);
161
162    m_screenLocation = IntPoint(screenX, screenY);
163    m_ctrlKey = ctrlKey;
164    m_altKey = altKey;
165    m_shiftKey = shiftKey;
166    m_metaKey = metaKey;
167    m_button = button == (unsigned short)-1 ? 0 : button;
168    m_buttonDown = button != (unsigned short)-1;
169    m_relatedTarget = relatedTarget;
170
171    initCoordinates(IntPoint(clientX, clientY));
172
173    // FIXME: m_isSimulated is not set to false here.
174    // FIXME: m_clipboard is not set to 0 here.
175}
176
177const AtomicString& MouseEvent::interfaceName() const
178{
179    return eventNames().interfaceForMouseEvent;
180}
181
182bool MouseEvent::isMouseEvent() const
183{
184    return true;
185}
186
187bool MouseEvent::isDragEvent() const
188{
189    const AtomicString& t = type();
190    return t == eventNames().dragenterEvent || t == eventNames().dragoverEvent || t == eventNames().dragleaveEvent || t == eventNames().dropEvent
191               || t == eventNames().dragstartEvent|| t == eventNames().dragEvent || t == eventNames().dragendEvent;
192}
193
194int MouseEvent::which() const
195{
196    // For the DOM, the return values for left, middle and right mouse buttons are 0, 1, 2, respectively.
197    // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively.
198    // So we must add 1.
199    if (!m_buttonDown)
200        return 0;
201    return m_button + 1;
202}
203
204Node* MouseEvent::toElement() const
205{
206    // MSIE extension - "the object toward which the user is moving the mouse pointer"
207    if (type() == eventNames().mouseoutEvent || type() == eventNames().mouseleaveEvent)
208        return relatedTarget() ? relatedTarget()->toNode() : 0;
209
210    return target() ? target()->toNode() : 0;
211}
212
213Node* MouseEvent::fromElement() const
214{
215    // MSIE extension - "object from which activation or the mouse pointer is exiting during the event" (huh?)
216    if (type() != eventNames().mouseoutEvent && type() != eventNames().mouseleaveEvent)
217        return relatedTarget() ? relatedTarget()->toNode() : 0;
218
219    return target() ? target()->toNode() : 0;
220}
221
222// FIXME: Fix positioning. e.g. We need to consider border/padding.
223// https://bugs.webkit.org/show_bug.cgi?id=93696
224inline static int adjustedClientX(int innerClientX, HTMLIFrameElement* iframe, FrameView* frameView)
225{
226    return iframe->offsetLeft() - frameView->scrollX() + innerClientX;
227}
228
229inline static int adjustedClientY(int innerClientY, HTMLIFrameElement* iframe, FrameView* frameView)
230{
231    return iframe->offsetTop() - frameView->scrollY() + innerClientY;
232}
233
234PassRefPtr<Event> MouseEvent::cloneFor(HTMLIFrameElement* iframe) const
235{
236    ASSERT(iframe);
237    RefPtr<MouseEvent> clonedMouseEvent = MouseEvent::create();
238    Frame* frame = iframe->document()->frame();
239    FrameView* frameView = frame ? frame->view() : 0;
240    clonedMouseEvent->initMouseEvent(type(), bubbles(), cancelable(),
241            iframe->document()->defaultView(),
242            detail(), screenX(), screenY(),
243            frameView ? adjustedClientX(clientX(), iframe, frameView) : 0,
244            frameView ? adjustedClientY(clientY(), iframe, frameView) : 0,
245            ctrlKey(), altKey(), shiftKey(), metaKey(),
246            button(),
247            // Nullifies relatedTarget.
248            0);
249    return clonedMouseEvent.release();
250}
251
252PassRefPtr<SimulatedMouseEvent> SimulatedMouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent)
253{
254    return adoptRef(new SimulatedMouseEvent(eventType, view, underlyingEvent));
255}
256
257SimulatedMouseEvent::~SimulatedMouseEvent()
258{
259}
260
261SimulatedMouseEvent::SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent)
262    : MouseEvent(eventType, true, true, view, 0, 0, 0, 0, 0,
263#if ENABLE(POINTER_LOCK)
264                 0, 0,
265#endif
266                 false, false, false, false, 0, 0, 0, true)
267{
268    if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) {
269        m_ctrlKey = keyStateEvent->ctrlKey();
270        m_altKey = keyStateEvent->altKey();
271        m_shiftKey = keyStateEvent->shiftKey();
272        m_metaKey = keyStateEvent->metaKey();
273    }
274    setUnderlyingEvent(underlyingEvent);
275
276    if (this->underlyingEvent() && this->underlyingEvent()->isMouseEvent()) {
277        MouseEvent* mouseEvent = static_cast<MouseEvent*>(this->underlyingEvent());
278        m_screenLocation = mouseEvent->screenLocation();
279        initCoordinates(mouseEvent->clientLocation());
280    }
281}
282
283PassRefPtr<MouseEventDispatchMediator> MouseEventDispatchMediator::create(PassRefPtr<MouseEvent> mouseEvent, MouseEventType mouseEventType)
284{
285    return adoptRef(new MouseEventDispatchMediator(mouseEvent, mouseEventType));
286}
287
288MouseEventDispatchMediator::MouseEventDispatchMediator(PassRefPtr<MouseEvent> mouseEvent, MouseEventType mouseEventType)
289    : EventDispatchMediator(mouseEvent), m_mouseEventType(mouseEventType)
290{
291}
292
293MouseEvent* MouseEventDispatchMediator::event() const
294{
295    return static_cast<MouseEvent*>(EventDispatchMediator::event());
296}
297
298bool MouseEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
299{
300    if (isSyntheticMouseEvent()) {
301        EventRetargeter::adjustForMouseEvent(dispatcher->node(), *event(),  dispatcher->eventPath());
302        return dispatcher->dispatch();
303    }
304
305    if (isDisabledFormControl(dispatcher->node()))
306        return false;
307
308    if (event()->type().isEmpty())
309        return true; // Shouldn't happen.
310
311    ASSERT(!event()->target() || event()->target() != event()->relatedTarget());
312
313    EventTarget* relatedTarget = event()->relatedTarget();
314    EventRetargeter::adjustForMouseEvent(dispatcher->node(), *event(),  dispatcher->eventPath());
315
316    dispatcher->dispatch();
317    bool swallowEvent = event()->defaultHandled() || event()->defaultPrevented();
318
319    if (event()->type() != eventNames().clickEvent || event()->detail() != 2)
320        return !swallowEvent;
321
322    // Special case: If it's a double click event, we also send the dblclick event. This is not part
323    // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated
324    // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same.
325    RefPtr<MouseEvent> doubleClickEvent = MouseEvent::create();
326    doubleClickEvent->initMouseEvent(eventNames().dblclickEvent, event()->bubbles(), event()->cancelable(), event()->view(),
327                                     event()->detail(), event()->screenX(), event()->screenY(), event()->clientX(), event()->clientY(),
328                                     event()->ctrlKey(), event()->altKey(), event()->shiftKey(), event()->metaKey(),
329                                     event()->button(), relatedTarget);
330    if (event()->defaultHandled())
331        doubleClickEvent->setDefaultHandled();
332    EventDispatcher::dispatchEvent(dispatcher->node(), MouseEventDispatchMediator::create(doubleClickEvent));
333    if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented())
334        return false;
335    return !swallowEvent;
336}
337
338} // namespace WebCore
339