1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MouseEvent.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/EventForwards.h"
11 #include "mozilla/MouseEvents.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 #include "mozilla/ViewportUtils.h"
15 #include "nsContentUtils.h"
17 #include "nsIScreenManager.h"
18 #include "nsLayoutUtils.h"
20 namespace mozilla::dom
{
22 static nsIntPoint
DevPixelsToCSSPixels(const LayoutDeviceIntPoint
& aPoint
,
23 nsPresContext
* aContext
) {
24 return nsIntPoint(aContext
->DevPixelsToIntCSSPixels(aPoint
.x
),
25 aContext
->DevPixelsToIntCSSPixels(aPoint
.y
));
28 MouseEvent::MouseEvent(EventTarget
* aOwner
, nsPresContext
* aPresContext
,
29 WidgetMouseEventBase
* aEvent
)
30 : UIEvent(aOwner
, aPresContext
,
32 : new WidgetMouseEvent(false, eVoidEvent
, nullptr,
33 WidgetMouseEvent::eReal
)) {
34 // There's no way to make this class' ctor allocate an WidgetMouseScrollEvent.
35 // It's not that important, though, since a scroll event is not a real
38 WidgetMouseEventBase
* const mouseEventBase
= mEvent
->AsMouseEventBase();
39 MOZ_ASSERT(mouseEventBase
);
41 mEventIsInternal
= false;
43 mEventIsInternal
= true;
44 mEvent
->mRefPoint
= LayoutDeviceIntPoint(0, 0);
45 mouseEventBase
->mInputSource
= MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
48 mUseFractionalCoords
= mouseEventBase
->DOMEventShouldUseFractionalCoords();
49 mWidgetRelativePoint
= mEvent
->mRefPoint
;
51 if (const WidgetMouseEvent
* mouseEvent
= mouseEventBase
->AsMouseEvent()) {
52 MOZ_ASSERT(mouseEvent
->mReason
!= WidgetMouseEvent::eSynthesized
,
53 "Don't dispatch DOM events from synthesized mouse events");
54 mDetail
= static_cast<int32_t>(mouseEvent
->mClickCount
);
58 void MouseEvent::InitMouseEventInternal(
59 const nsAString
& aType
, bool aCanBubble
, bool aCancelable
,
60 nsGlobalWindowInner
* aView
, int32_t aDetail
, double aScreenX
,
61 double aScreenY
, double aClientX
, double aClientY
, bool aCtrlKey
,
62 bool aAltKey
, bool aShiftKey
, bool aMetaKey
, uint16_t aButton
,
63 EventTarget
* aRelatedTarget
) {
64 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
66 UIEvent::InitUIEvent(aType
, aCanBubble
, aCancelable
, aView
, aDetail
);
68 switch (mEvent
->mClass
) {
69 case eMouseEventClass
:
70 case eMouseScrollEventClass
:
71 case eWheelEventClass
:
73 case ePointerEventClass
:
74 case eSimpleGestureEventClass
: {
75 WidgetMouseEventBase
* mouseEventBase
= mEvent
->AsMouseEventBase();
76 mouseEventBase
->mRelatedTarget
= aRelatedTarget
;
77 mouseEventBase
->mButton
= aButton
;
78 mouseEventBase
->InitBasicModifiers(aCtrlKey
, aAltKey
, aShiftKey
,
80 mDefaultClientPoint
= CSSDoublePoint(aClientX
, aClientY
);
81 mWidgetRelativePoint
= LayoutDeviceDoublePoint(aScreenX
, aScreenY
);
82 mouseEventBase
->mRefPoint
=
83 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
);
85 WidgetMouseEvent
* mouseEvent
= mEvent
->AsMouseEvent();
87 mouseEvent
->mClickCount
= aDetail
;
90 mUseFractionalCoords
=
91 mouseEventBase
->DOMEventShouldUseFractionalCoords();
92 if (!mUseFractionalCoords
) {
93 // If we should not use fractional coordinates for this event, we need
94 // to drop the fractional part as defined for the backward compatibility
95 // when we treated the input values are integer coordinates. These
96 // values will be exposed as screenX, screenY, clientX and clientY as-is
97 // too. That matches with the Pointer Events spec definitions too.
98 // https://w3c.github.io/pointerevents/#event-coordinates
99 mDefaultClientPoint
= CSSIntPoint::Floor(mDefaultClientPoint
);
100 mWidgetRelativePoint
=
101 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
);
110 void MouseEvent::InitMouseEventInternal(
111 const nsAString
& aType
, bool aCanBubble
, bool aCancelable
,
112 nsGlobalWindowInner
* aView
, int32_t aDetail
, double aScreenX
,
113 double aScreenY
, double aClientX
, double aClientY
, int16_t aButton
,
114 EventTarget
* aRelatedTarget
, const nsAString
& aModifiersList
) {
115 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
117 Modifiers modifiers
= ComputeModifierState(aModifiersList
);
119 InitMouseEventInternal(
120 aType
, aCanBubble
, aCancelable
, aView
, aDetail
, aScreenX
, aScreenY
,
121 aClientX
, aClientY
, (modifiers
& MODIFIER_CONTROL
) != 0,
122 (modifiers
& MODIFIER_ALT
) != 0, (modifiers
& MODIFIER_SHIFT
) != 0,
123 (modifiers
& MODIFIER_META
) != 0, aButton
, aRelatedTarget
);
125 switch (mEvent
->mClass
) {
126 case eMouseEventClass
:
127 case eMouseScrollEventClass
:
128 case eWheelEventClass
:
129 case eDragEventClass
:
130 case ePointerEventClass
:
131 case eSimpleGestureEventClass
:
132 mEvent
->AsInputEvent()->mModifiers
= modifiers
;
135 MOZ_CRASH("There is no space to store the modifiers");
139 void MouseEvent::InitializeExtraMouseEventDictionaryMembers(
140 const MouseEventInit
& aParam
) {
141 InitModifiers(aParam
);
142 mEvent
->AsMouseEventBase()->mButtons
= aParam
.mButtons
;
143 mMovementPoint
.x
= aParam
.mMovementX
;
144 mMovementPoint
.y
= aParam
.mMovementY
;
147 already_AddRefed
<MouseEvent
> MouseEvent::Constructor(
148 const GlobalObject
& aGlobal
, const nsAString
& aType
,
149 const MouseEventInit
& aParam
) {
150 nsCOMPtr
<EventTarget
> t
= do_QueryInterface(aGlobal
.GetAsSupports());
151 RefPtr
<MouseEvent
> e
= new MouseEvent(t
, nullptr, nullptr);
152 bool trusted
= e
->Init(t
);
153 e
->InitMouseEventInternal(
154 aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
, aParam
.mDetail
,
155 aParam
.mScreenX
, aParam
.mScreenY
, aParam
.mClientX
, aParam
.mClientY
,
156 aParam
.mCtrlKey
, aParam
.mAltKey
, aParam
.mShiftKey
, aParam
.mMetaKey
,
157 aParam
.mButton
, aParam
.mRelatedTarget
);
158 e
->InitializeExtraMouseEventDictionaryMembers(aParam
);
159 e
->SetTrusted(trusted
);
160 e
->SetComposed(aParam
.mComposed
);
161 MOZ_ASSERT(!trusted
|| !IsPointerEventMessage(e
->mEvent
->mMessage
),
162 "Please use PointerEvent constructor!");
166 void MouseEvent::InitNSMouseEvent(const nsAString
& aType
, bool aCanBubble
,
167 bool aCancelable
, nsGlobalWindowInner
* aView
,
168 int32_t aDetail
, int32_t aScreenX
,
169 int32_t aScreenY
, int32_t aClientX
,
170 int32_t aClientY
, bool aCtrlKey
, bool aAltKey
,
171 bool aShiftKey
, bool aMetaKey
,
172 uint16_t aButton
, EventTarget
* aRelatedTarget
,
173 float aPressure
, uint16_t aInputSource
) {
174 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
176 InitMouseEventInternal(aType
, aCanBubble
, aCancelable
, aView
, aDetail
,
177 aScreenX
, aScreenY
, aClientX
, aClientY
, aCtrlKey
,
178 aAltKey
, aShiftKey
, aMetaKey
, aButton
, aRelatedTarget
);
180 WidgetMouseEventBase
* mouseEventBase
= mEvent
->AsMouseEventBase();
181 mouseEventBase
->mPressure
= aPressure
;
182 mouseEventBase
->mInputSource
= aInputSource
;
185 void MouseEvent::DuplicatePrivateData() {
186 // If this is a event not created from WidgetMouseEventBase or its subclasses
187 // (i.e., created by JS), mDefaultClientPoint and mMovementPoint are
188 // initialized as expected values. Therefore, we don't need to recompute it.
189 if (!mEventIsInternal
) {
190 mDefaultClientPoint
= ClientPoint();
191 mMovementPoint
= GetMovementPoint();
193 // However, mPagePoint needs to include the scroll position. Therefore, we
194 // need to compute here.
195 mPagePoint
= PagePoint();
197 // mEvent->mRefPoint is computed by UIEvent::DuplicatePrivateData() with
198 // the device pixel scale, but if we need to store fractional values to
199 // mWidgetRelativePoint, we need to do same thing by ourselves.
200 Maybe
<const CSSDoublePoint
> maybeScreenPoint
;
201 if (mUseFractionalCoords
) {
202 maybeScreenPoint
.emplace(ScreenPoint(CallerType::System
));
204 UIEvent::DuplicatePrivateData();
205 if (maybeScreenPoint
.isSome()) {
206 CSSToLayoutDeviceScale scale
= mPresContext
207 ? mPresContext
->CSSToDevPixelScale()
208 : CSSToLayoutDeviceScale(1);
209 mWidgetRelativePoint
= maybeScreenPoint
.ref() * scale
;
211 // As mentioned above, mEvent->mRefPoint is already computed by UIEvent, so,
212 // do not need to compute the scale.
213 mWidgetRelativePoint
= mEvent
->mRefPoint
;
217 void MouseEvent::PreventClickEvent() {
218 if (WidgetMouseEvent
* mouseEvent
= mEvent
->AsMouseEvent()) {
219 mouseEvent
->mClickEventPrevented
= true;
223 bool MouseEvent::ClickEventPrevented() {
224 if (WidgetMouseEvent
* mouseEvent
= mEvent
->AsMouseEvent()) {
225 return mouseEvent
->mClickEventPrevented
;
230 int16_t MouseEvent::Button() {
231 switch (mEvent
->mClass
) {
232 case eMouseEventClass
:
233 case eMouseScrollEventClass
:
234 case eWheelEventClass
:
235 case eDragEventClass
:
236 case ePointerEventClass
:
237 case eSimpleGestureEventClass
:
238 return mEvent
->AsMouseEventBase()->mButton
;
240 NS_WARNING("Tried to get mouse mButton for non-mouse event!");
241 return MouseButton::ePrimary
;
245 uint16_t MouseEvent::Buttons() {
246 switch (mEvent
->mClass
) {
247 case eMouseEventClass
:
248 case eMouseScrollEventClass
:
249 case eWheelEventClass
:
250 case eDragEventClass
:
251 case ePointerEventClass
:
252 case eSimpleGestureEventClass
:
253 return mEvent
->AsMouseEventBase()->mButtons
;
255 MOZ_CRASH("Tried to get mouse buttons for non-mouse event!");
259 already_AddRefed
<EventTarget
> MouseEvent::GetRelatedTarget() {
260 nsCOMPtr
<EventTarget
> relatedTarget
;
261 switch (mEvent
->mClass
) {
262 case eMouseEventClass
:
263 case eMouseScrollEventClass
:
264 case eWheelEventClass
:
265 case eDragEventClass
:
266 case ePointerEventClass
:
267 case eSimpleGestureEventClass
:
268 relatedTarget
= mEvent
->AsMouseEventBase()->mRelatedTarget
;
274 return EnsureWebAccessibleRelatedTarget(relatedTarget
);
277 CSSDoublePoint
MouseEvent::ScreenPoint(CallerType aCallerType
) const {
278 if (mEvent
->mFlags
.mIsPositionless
) {
282 // If this is a trusted event, mWidgetRelativeOffset is a copy of
283 // mEvent->mRefPoint, so, the values are integer.
284 // If this is an untrusted event, mWidgetRelativeOffset should be floored when
286 MOZ_ASSERT_IF(!mUseFractionalCoords
,
287 mWidgetRelativePoint
==
288 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
));
289 if (nsContentUtils::ShouldResistFingerprinting(
290 aCallerType
, GetParentObject(), RFPTarget::MouseEventScreenPoint
)) {
291 // Sanitize to something sort of like client coords, but not quite
292 // (defaulting to (0,0) instead of our pre-specified client coords).
293 const CSSDoublePoint clientPoint
= Event::GetClientCoords(
294 mPresContext
, mEvent
, mWidgetRelativePoint
, CSSDoublePoint
{0, 0});
295 return mUseFractionalCoords
? clientPoint
: RoundedToInt(clientPoint
);
298 const CSSDoublePoint screenPoint
=
299 Event::GetScreenCoords(mPresContext
, mEvent
, mWidgetRelativePoint
)
301 return mUseFractionalCoords
? screenPoint
: RoundedToInt(screenPoint
);
304 LayoutDeviceIntPoint
MouseEvent::ScreenPointLayoutDevicePix() const {
305 const CSSDoublePoint point
= ScreenPoint(CallerType::System
);
306 auto scale
= mPresContext
? mPresContext
->CSSToDevPixelScale()
307 : CSSToLayoutDeviceScale();
308 return LayoutDeviceIntPoint::Round(point
* scale
);
311 DesktopIntPoint
MouseEvent::ScreenPointDesktopPix() const {
312 const CSSDoublePoint point
= ScreenPoint(CallerType::System
);
315 ? mPresContext
->CSSToDevPixelScale() /
316 mPresContext
->DeviceContext()->GetDesktopToDeviceScale()
317 : CSSToDesktopScale();
318 return DesktopIntPoint::Round(point
* scale
);
321 already_AddRefed
<nsIScreen
> MouseEvent::GetScreen() {
322 nsCOMPtr
<nsIScreenManager
> screenMgr
=
323 do_GetService("@mozilla.org/gfx/screenmanager;1");
327 return screenMgr
->ScreenForRect(
328 DesktopIntRect(ScreenPointDesktopPix(), DesktopIntSize(1, 1)));
331 CSSDoublePoint
MouseEvent::PagePoint() const {
332 if (mEvent
->mFlags
.mIsPositionless
) {
336 if (mPrivateDataDuplicated
) {
337 // mPagePoint should be floored when it started to cache the values after
339 MOZ_ASSERT_IF(!mUseFractionalCoords
,
340 mPagePoint
== CSSIntPoint::Floor(mPagePoint
));
344 // If this is a trusted event, mWidgetRelativeOffset is a copy of
345 // mEvent->mRefPoint, so, the values are integer.
346 // If this is an untrusted event, mWidgetRelativeOffset should be floored when
348 MOZ_ASSERT_IF(!mUseFractionalCoords
,
349 mWidgetRelativePoint
==
350 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
));
351 // If this is a trusted event, mDefaultClientPoint should be floored when
352 // it started to cache the values after the propagation.
353 // If this is an untrusted event, mDefaultClientPoint should be floored when
355 MOZ_ASSERT_IF(!mUseFractionalCoords
,
356 mDefaultClientPoint
== CSSIntPoint::Floor(mDefaultClientPoint
));
357 const CSSDoublePoint pagePoint
= Event::GetPageCoords(
358 mPresContext
, mEvent
, mWidgetRelativePoint
, mDefaultClientPoint
);
359 return mUseFractionalCoords
? pagePoint
: RoundedToInt(pagePoint
);
362 CSSDoublePoint
MouseEvent::ClientPoint() const {
363 if (mEvent
->mFlags
.mIsPositionless
) {
367 // If this is a trusted event, mWidgetRelativeOffset is a copy of
368 // mEvent->mRefPoint, so, the values are integer.
369 // If this is an untrusted event, mWidgetRelativeOffset should be floored when
371 MOZ_ASSERT_IF(!mUseFractionalCoords
,
372 mWidgetRelativePoint
==
373 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
));
374 // If this is a trusted event, mDefaultClientPoint should be floored when
375 // it started to cache the values after the propagation.
376 // If this is an untrusted event, mDefaultClientPoint should be floored when
378 MOZ_ASSERT_IF(!mUseFractionalCoords
,
379 mDefaultClientPoint
== CSSIntPoint::Floor(mDefaultClientPoint
));
380 const CSSDoublePoint clientPoint
= Event::GetClientCoords(
381 mPresContext
, mEvent
, mWidgetRelativePoint
, mDefaultClientPoint
);
382 return mUseFractionalCoords
? clientPoint
: RoundedToInt(clientPoint
);
385 CSSDoublePoint
MouseEvent::OffsetPoint() const {
386 if (mEvent
->mFlags
.mIsPositionless
) {
390 // If this is a trusted event, mWidgetRelativeOffset is a copy of
391 // mEvent->mRefPoint, so, the values are integer.
392 // If this is an untrusted event, mWidgetRelativeOffset should be floored when
394 MOZ_ASSERT_IF(!mUseFractionalCoords
,
395 mWidgetRelativePoint
==
396 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint
));
397 // If this is a trusted event, mDefaultClientPoint should be floored when
398 // it started to cache the values after the propagation.
399 // If this is an untrusted event, mDefaultClientPoint should be floored when
401 MOZ_ASSERT_IF(!mUseFractionalCoords
,
402 mDefaultClientPoint
== CSSIntPoint::Floor(mDefaultClientPoint
));
403 RefPtr
<nsPresContext
> presContext(mPresContext
);
404 const CSSDoublePoint offsetPoint
= Event::GetOffsetCoords(
405 presContext
, mEvent
, mWidgetRelativePoint
, mDefaultClientPoint
);
406 return mUseFractionalCoords
? offsetPoint
: RoundedToInt(offsetPoint
);
409 nsIntPoint
MouseEvent::GetMovementPoint() const {
410 if (mEvent
->mFlags
.mIsPositionless
) {
411 return nsIntPoint(0, 0);
414 if (mPrivateDataDuplicated
|| mEventIsInternal
) {
415 return mMovementPoint
;
418 if (!mEvent
|| !mEvent
->AsGUIEvent()->mWidget
||
419 (mEvent
->mMessage
!= eMouseMove
&& mEvent
->mMessage
!= ePointerMove
)) {
420 // Pointer Lock spec defines that movementX/Y must be zero for all mouse
421 // events except mousemove.
422 return nsIntPoint(0, 0);
425 // Calculate the delta between the last screen point and the current one.
426 nsIntPoint current
= DevPixelsToCSSPixels(mEvent
->mRefPoint
, mPresContext
);
427 nsIntPoint last
= DevPixelsToCSSPixels(mEvent
->mLastRefPoint
, mPresContext
);
428 return current
- last
;
431 bool MouseEvent::AltKey() { return mEvent
->AsInputEvent()->IsAlt(); }
433 bool MouseEvent::CtrlKey() { return mEvent
->AsInputEvent()->IsControl(); }
435 bool MouseEvent::ShiftKey() { return mEvent
->AsInputEvent()->IsShift(); }
437 bool MouseEvent::MetaKey() { return mEvent
->AsInputEvent()->IsMeta(); }
439 float MouseEvent::MozPressure(CallerType aCallerType
) const {
440 if (nsContentUtils::ShouldResistFingerprinting(aCallerType
, GetParentObject(),
441 RFPTarget::PointerEvents
)) {
442 // Use the spoofed value from PointerEvent::Pressure
446 return mEvent
->AsMouseEventBase()->mPressure
;
449 uint16_t MouseEvent::InputSource(CallerType aCallerType
) const {
450 if (nsContentUtils::ShouldResistFingerprinting(aCallerType
, GetParentObject(),
451 RFPTarget::PointerEvents
)) {
452 return MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
455 return mEvent
->AsMouseEventBase()->mInputSource
;
458 } // namespace mozilla::dom
460 using namespace mozilla
;
461 using namespace mozilla::dom
;
463 already_AddRefed
<MouseEvent
> NS_NewDOMMouseEvent(EventTarget
* aOwner
,
464 nsPresContext
* aPresContext
,
465 WidgetMouseEvent
* aEvent
) {
466 RefPtr
<MouseEvent
> it
= new MouseEvent(aOwner
, aPresContext
, aEvent
);