Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / events / MouseEvent.cpp
blobf95b30f431db26e6b98bd0d0acdf9e2104bd4981
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"
16 #include "nsIFrame.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,
31 aEvent ? aEvent
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
36 // DOM event.
38 WidgetMouseEventBase* const mouseEventBase = mEvent->AsMouseEventBase();
39 MOZ_ASSERT(mouseEventBase);
40 if (aEvent) {
41 mEventIsInternal = false;
42 } else {
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:
72 case eDragEventClass:
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,
79 aMetaKey);
80 mDefaultClientPoint = CSSDoublePoint(aClientX, aClientY);
81 mWidgetRelativePoint = LayoutDeviceDoublePoint(aScreenX, aScreenY);
82 mouseEventBase->mRefPoint =
83 LayoutDeviceIntPoint::Floor(mWidgetRelativePoint);
85 WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
86 if (mouseEvent) {
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);
103 break;
105 default:
106 break;
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;
133 return;
134 default:
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!");
163 return e.forget();
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;
210 } else {
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;
227 return false;
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;
239 default:
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;
254 default:
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;
269 break;
270 default:
271 break;
274 return EnsureWebAccessibleRelatedTarget(relatedTarget);
277 CSSDoublePoint MouseEvent::ScreenPoint(CallerType aCallerType) const {
278 if (mEvent->mFlags.mIsPositionless) {
279 return {};
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
285 // it's initialized.
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)
300 .extract();
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);
313 auto scale =
314 mPresContext
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");
324 if (!screenMgr) {
325 return nullptr;
327 return screenMgr->ScreenForRect(
328 DesktopIntRect(ScreenPointDesktopPix(), DesktopIntSize(1, 1)));
331 CSSDoublePoint MouseEvent::PagePoint() const {
332 if (mEvent->mFlags.mIsPositionless) {
333 return {};
336 if (mPrivateDataDuplicated) {
337 // mPagePoint should be floored when it started to cache the values after
338 // the propagation.
339 MOZ_ASSERT_IF(!mUseFractionalCoords,
340 mPagePoint == CSSIntPoint::Floor(mPagePoint));
341 return 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
347 // it's initialized.
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
354 // it's initialized.
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) {
364 return {};
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
370 // it's initialized.
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
377 // it's initialized.
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) {
387 return {};
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
393 // it's initialized.
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
400 // it's initialized.
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
443 return 0.5;
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);
467 return it.forget();