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-2016 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 "JSDOMConvertInterface.h"
32#include "JSDOMConvertNullable.h"
33#include "JSEventTarget.h"
34#include "JSEventTargetCustom.h"
35#include "PlatformMouseEvent.h"
36#include "RuntimeApplicationChecks.h"
37
38namespace WebCore {
39
40using namespace JSC;
41
42Ref<MouseEvent> MouseEvent::create(const AtomString& type, const MouseEventInit& initializer)
43{
44 return adoptRef(*new MouseEvent(type, initializer));
45}
46
47Ref<MouseEvent> MouseEvent::create(const AtomString& eventType, RefPtr<WindowProxy>&& view, const PlatformMouseEvent& event, int detail, Node* relatedTarget)
48{
49 bool isMouseEnterOrLeave = eventType == eventNames().mouseenterEvent || eventType == eventNames().mouseleaveEvent;
50 auto isCancelable = eventType != eventNames().mousemoveEvent && !isMouseEnterOrLeave ? IsCancelable::Yes : IsCancelable::No;
51 auto canBubble = !isMouseEnterOrLeave ? CanBubble::Yes : CanBubble::No;
52 auto isComposed = !isMouseEnterOrLeave ? IsComposed::Yes : IsComposed::No;
53
54 return MouseEvent::create(eventType, canBubble, isCancelable, isComposed, event.timestamp().approximateMonotonicTime(), WTFMove(view), detail,
55 event.globalPosition(), event.position(),
56#if ENABLE(POINTER_LOCK)
57 event.movementDelta(),
58#else
59 { },
60#endif
61 event.modifiers(), event.button(), event.buttons(), relatedTarget, event.force(), event.syntheticClickType());
62}
63
64Ref<MouseEvent> MouseEvent::create(const AtomString& type, CanBubble canBubble, IsCancelable isCancelable, IsComposed isComposed, MonotonicTime timestamp, RefPtr<WindowProxy>&& view, int detail,
65 const IntPoint& screenLocation, const IntPoint& windowLocation, const IntPoint& movementDelta, OptionSet<Modifier> modifiers, short button, unsigned short buttons,
66 EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer* dataTransfer, IsSimulated isSimulated, IsTrusted isTrusted)
67{
68 return adoptRef(*new MouseEvent(type, canBubble, isCancelable, isComposed, timestamp, WTFMove(view), detail,
69 screenLocation, windowLocation, movementDelta, modifiers, button, buttons, relatedTarget, force, syntheticClickType, dataTransfer, isSimulated, isTrusted));
70}
71
72Ref<MouseEvent> MouseEvent::create(const AtomString& eventType, CanBubble canBubble, IsCancelable isCancelable, IsComposed isComposed, RefPtr<WindowProxy>&& view, int detail,
73 int screenX, int screenY, int clientX, int clientY, OptionSet<Modifier> modifiers, short button, unsigned short buttons,
74 unsigned short syntheticClickType, EventTarget* relatedTarget)
75{
76 return adoptRef(*new MouseEvent(eventType, canBubble, isCancelable, isComposed, WTFMove(view), detail, { screenX, screenY }, { clientX, clientY }, modifiers, button, buttons, syntheticClickType, relatedTarget));
77}
78
79MouseEvent::MouseEvent() = default;
80
81MouseEvent::MouseEvent(const AtomString& eventType, CanBubble canBubble, IsCancelable isCancelable, IsComposed isComposed,
82 MonotonicTime timestamp, RefPtr<WindowProxy>&& view, int detail,
83 const IntPoint& screenLocation, const IntPoint& windowLocation, const IntPoint& movementDelta, OptionSet<Modifier> modifiers, short button, unsigned short buttons,
84 EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer* dataTransfer, IsSimulated isSimulated, IsTrusted isTrusted)
85 : MouseRelatedEvent(eventType, canBubble, isCancelable, isComposed, timestamp, WTFMove(view), detail, screenLocation, windowLocation, movementDelta, modifiers, isSimulated, isTrusted)
86 , m_button(button == -2 ? 0 : button)
87 , m_buttons(buttons)
88 , m_syntheticClickType(button == -2 ? 0 : syntheticClickType)
89 , m_buttonDown(button != -2)
90 , m_relatedTarget(relatedTarget)
91 , m_force(force)
92 , m_dataTransfer(dataTransfer)
93{
94}
95
96MouseEvent::MouseEvent(const AtomString& eventType, CanBubble canBubble, IsCancelable isCancelable, IsComposed isComposed,
97 RefPtr<WindowProxy>&& view, int detail, const IntPoint& screenLocation, const IntPoint& clientLocation,
98 OptionSet<Modifier> modifiers, short button, unsigned short buttons, unsigned short syntheticClickType, EventTarget* relatedTarget)
99 : MouseRelatedEvent(eventType, canBubble, isCancelable, isComposed, MonotonicTime::now(), WTFMove(view), detail, screenLocation, { }, { }, modifiers, IsSimulated::No)
100 , m_button(button == -2 ? 0 : button)
101 , m_buttons(buttons)
102 , m_syntheticClickType(button == -2 ? 0 : syntheticClickType)
103 , m_buttonDown(button != -2)
104 , m_relatedTarget(relatedTarget)
105{
106 initCoordinates(clientLocation);
107}
108
109MouseEvent::MouseEvent(const AtomString& eventType, const MouseEventInit& initializer)
110 : MouseRelatedEvent(eventType, initializer)
111 , m_button(initializer.button == -2 ? 0 : initializer.button)
112 , m_buttons(initializer.buttons)
113 , m_buttonDown(initializer.button != -2)
114 , m_relatedTarget(initializer.relatedTarget)
115{
116 initCoordinates({ initializer.clientX, initializer.clientY });
117}
118
119MouseEvent::~MouseEvent() = default;
120
121void MouseEvent::initMouseEvent(const AtomString& type, bool canBubble, bool cancelable, RefPtr<WindowProxy>&& view, int detail,
122 int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, short button, EventTarget* relatedTarget)
123{
124 if (isBeingDispatched())
125 return;
126
127 initUIEvent(type, canBubble, cancelable, WTFMove(view), detail);
128
129 m_screenLocation = IntPoint(screenX, screenY);
130 setModifierKeys(ctrlKey, altKey, shiftKey, metaKey);
131 m_button = button == -2 ? 0 : button;
132 m_syntheticClickType = 0;
133 m_buttonDown = button != -2;
134 m_relatedTarget = relatedTarget;
135
136 initCoordinates(IntPoint(clientX, clientY));
137
138 setIsSimulated(false);
139 m_dataTransfer = nullptr;
140}
141
142// FIXME: We need this quirk because iAd Producer is calling this function with a relatedTarget that is not an EventTarget (rdar://problem/30640101).
143// We should remove this quirk when possible.
144void MouseEvent::initMouseEventQuirk(ExecState& state, ScriptExecutionContext& scriptExecutionContext, const AtomString& type, bool canBubble, bool cancelable, RefPtr<WindowProxy>&& view, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, short button, JSValue relatedTargetValue)
145{
146 EventTarget* relatedTarget = nullptr;
147#if PLATFORM(MAC)
148 // Impacts iBooks too because of widgets generated by iAd Producer (rdar://problem/30797958).
149 if (MacApplication::isIAdProducer() || MacApplication::isIBooks()) {
150 // jsEventTargetCast() does not throw and will silently convert bad input to nullptr.
151 auto jsRelatedTarget = jsEventTargetCast(state.vm(), relatedTargetValue);
152 if (!jsRelatedTarget && !relatedTargetValue.isUndefinedOrNull())
153 scriptExecutionContext.addConsoleMessage(MessageSource::JS, MessageLevel::Warning, "Calling initMouseEvent() with a relatedTarget that is not an EventTarget is deprecated."_s);
154 relatedTarget = jsRelatedTarget ? &jsRelatedTarget->wrapped() : nullptr;
155 } else {
156#else
157 UNUSED_PARAM(scriptExecutionContext);
158#endif
159 // This is what the bindings generator would have produced.
160 auto throwScope = DECLARE_THROW_SCOPE(state.vm());
161 relatedTarget = convert<IDLNullable<IDLInterface<EventTarget>>>(state, relatedTargetValue, [](ExecState& state, ThrowScope& scope) {
162 throwArgumentTypeError(state, scope, 14, "relatedTarget", "MouseEvent", "initMouseEvent", "EventTarget");
163 });
164 RETURN_IF_EXCEPTION(throwScope, void());
165#if PLATFORM(MAC)
166 }
167#endif
168 initMouseEvent(type, canBubble, cancelable, WTFMove(view), detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
169}
170
171EventInterface MouseEvent::eventInterface() const
172{
173 return MouseEventInterfaceType;
174}
175
176bool MouseEvent::isMouseEvent() const
177{
178 return true;
179}
180
181bool MouseEvent::isDragEvent() const
182{
183 // This function is only used to decide to return nullptr for dataTransfer even when m_dataTransfer is non-null.
184 // FIXME: Is that really valuable? Why will m_dataTransfer be non-null but we need to return null for dataTransfer?
185 // Quite peculiar to decide based on the type string; may have been be provided by call to JavaScript constructor.
186 auto& type = this->type();
187 return type == eventNames().dragEvent
188 || type == eventNames().dragendEvent
189 || type == eventNames().dragenterEvent
190 || type == eventNames().dragleaveEvent
191 || type == eventNames().dragoverEvent
192 || type == eventNames().dragstartEvent
193 || type == eventNames().dropEvent;
194}
195
196bool MouseEvent::canTriggerActivationBehavior(const Event& event)
197{
198 return event.type() == eventNames().clickEvent && (!is<MouseEvent>(event) || downcast<MouseEvent>(event).button() != RightButton);
199}
200
201int MouseEvent::which() const
202{
203 // For the DOM, the return values for left, middle and right mouse buttons are 0, 1, 2, respectively.
204 // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively.
205 // So we must add 1.
206 if (!m_buttonDown)
207 return 0;
208 return m_button + 1;
209}
210
211RefPtr<Node> MouseEvent::toElement() const
212{
213 // MSIE extension - "the object toward which the user is moving the mouse pointer"
214 EventTarget* target;
215 if (type() == eventNames().mouseoutEvent || type() == eventNames().mouseleaveEvent)
216 target = relatedTarget();
217 else
218 target = this->target();
219 return is<Node>(target) ? &downcast<Node>(*target) : nullptr;
220}
221
222RefPtr<Node> MouseEvent::fromElement() const
223{
224 // MSIE extension - "object from which activation or the mouse pointer is exiting during the event" (huh?)
225 EventTarget* target;
226 if (type() == eventNames().mouseoutEvent || type() == eventNames().mouseleaveEvent)
227 target = this->target();
228 else
229 target = relatedTarget();
230 return is<Node>(target) ? &downcast<Node>(*target) : nullptr;
231}
232
233} // namespace WebCore
234