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 * Portions Copyright 2013 Microsoft Open Technologies, Inc. */
9 #include "mozilla/dom/MouseEventBinding.h"
10 #include "mozilla/dom/PointerEvent.h"
11 #include "mozilla/dom/PointerEventBinding.h"
12 #include "mozilla/dom/PointerEventHandler.h"
13 #include "mozilla/MouseEvents.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "nsContentUtils.h"
17 #include "jsfriendapi.h"
19 namespace mozilla::dom
{
21 PointerEvent::PointerEvent(EventTarget
* aOwner
, nsPresContext
* aPresContext
,
22 WidgetPointerEvent
* aEvent
)
23 : MouseEvent(aOwner
, aPresContext
,
25 : new WidgetPointerEvent(false, eVoidEvent
, nullptr)) {
26 NS_ASSERTION(mEvent
->mClass
== ePointerEventClass
,
27 "event type mismatch ePointerEventClass");
29 WidgetMouseEvent
* mouseEvent
= mEvent
->AsMouseEvent();
31 mEventIsInternal
= false;
33 mEventIsInternal
= true;
34 mEvent
->mRefPoint
= LayoutDeviceIntPoint(0, 0);
35 mouseEvent
->mInputSource
= MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
37 // 5.2 Pointer Event types, for all pointer events, |detail| attribute SHOULD
42 JSObject
* PointerEvent::WrapObjectInternal(JSContext
* aCx
,
43 JS::Handle
<JSObject
*> aGivenProto
) {
44 return PointerEvent_Binding::Wrap(aCx
, this, aGivenProto
);
47 static uint16_t ConvertStringToPointerType(const nsAString
& aPointerTypeArg
) {
48 if (aPointerTypeArg
.EqualsLiteral("mouse")) {
49 return MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
51 if (aPointerTypeArg
.EqualsLiteral("pen")) {
52 return MouseEvent_Binding::MOZ_SOURCE_PEN
;
54 if (aPointerTypeArg
.EqualsLiteral("touch")) {
55 return MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
58 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
61 void ConvertPointerTypeToString(uint16_t aPointerTypeSrc
,
62 nsAString
& aPointerTypeDest
) {
63 switch (aPointerTypeSrc
) {
64 case MouseEvent_Binding::MOZ_SOURCE_MOUSE
:
65 aPointerTypeDest
.AssignLiteral("mouse");
67 case MouseEvent_Binding::MOZ_SOURCE_PEN
:
68 aPointerTypeDest
.AssignLiteral("pen");
70 case MouseEvent_Binding::MOZ_SOURCE_TOUCH
:
71 aPointerTypeDest
.AssignLiteral("touch");
74 aPointerTypeDest
.Truncate();
80 already_AddRefed
<PointerEvent
> PointerEvent::Constructor(
81 EventTarget
* aOwner
, const nsAString
& aType
,
82 const PointerEventInit
& aParam
) {
83 RefPtr
<PointerEvent
> e
= new PointerEvent(aOwner
, nullptr, nullptr);
84 bool trusted
= e
->Init(aOwner
);
86 e
->InitMouseEvent(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
87 aParam
.mDetail
, aParam
.mScreenX
, aParam
.mScreenY
,
88 aParam
.mClientX
, aParam
.mClientY
, false, false, false,
89 false, aParam
.mButton
, aParam
.mRelatedTarget
);
90 e
->InitializeExtraMouseEventDictionaryMembers(aParam
);
91 e
->mPointerType
= Some(aParam
.mPointerType
);
93 WidgetPointerEvent
* widgetEvent
= e
->mEvent
->AsPointerEvent();
94 widgetEvent
->pointerId
= aParam
.mPointerId
;
95 widgetEvent
->mWidth
= aParam
.mWidth
;
96 widgetEvent
->mHeight
= aParam
.mHeight
;
97 widgetEvent
->mPressure
= aParam
.mPressure
;
98 widgetEvent
->tangentialPressure
= aParam
.mTangentialPressure
;
99 widgetEvent
->tiltX
= aParam
.mTiltX
;
100 widgetEvent
->tiltY
= aParam
.mTiltY
;
101 widgetEvent
->twist
= aParam
.mTwist
;
102 widgetEvent
->mInputSource
= ConvertStringToPointerType(aParam
.mPointerType
);
103 widgetEvent
->mIsPrimary
= aParam
.mIsPrimary
;
104 widgetEvent
->mButtons
= aParam
.mButtons
;
106 if (!aParam
.mCoalescedEvents
.IsEmpty()) {
107 e
->mCoalescedEvents
.AppendElements(aParam
.mCoalescedEvents
);
109 if (!aParam
.mPredictedEvents
.IsEmpty()) {
110 e
->mPredictedEvents
.AppendElements(aParam
.mPredictedEvents
);
112 e
->SetTrusted(trusted
);
113 e
->SetComposed(aParam
.mComposed
);
118 already_AddRefed
<PointerEvent
> PointerEvent::Constructor(
119 const GlobalObject
& aGlobal
, const nsAString
& aType
,
120 const PointerEventInit
& aParam
) {
121 nsCOMPtr
<EventTarget
> owner
= do_QueryInterface(aGlobal
.GetAsSupports());
122 return Constructor(owner
, aType
, aParam
);
125 NS_IMPL_CYCLE_COLLECTION_CLASS(PointerEvent
)
127 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PointerEvent
, MouseEvent
)
128 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCoalescedEvents
)
129 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPredictedEvents
)
130 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
132 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PointerEvent
, MouseEvent
)
133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCoalescedEvents
)
134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPredictedEvents
)
135 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
137 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PointerEvent
)
138 NS_INTERFACE_MAP_END_INHERITING(MouseEvent
)
140 NS_IMPL_ADDREF_INHERITED(PointerEvent
, MouseEvent
)
141 NS_IMPL_RELEASE_INHERITED(PointerEvent
, MouseEvent
)
143 void PointerEvent::GetPointerType(nsAString
& aPointerType
) {
144 if (mPointerType
.isSome()) {
145 aPointerType
= mPointerType
.value();
149 if (ShouldResistFingerprinting()) {
150 aPointerType
.AssignLiteral("mouse");
154 ConvertPointerTypeToString(mEvent
->AsPointerEvent()->mInputSource
,
158 int32_t PointerEvent::PointerId() {
159 return ShouldResistFingerprinting()
160 ? PointerEventHandler::GetSpoofedPointerIdForRFP()
161 : mEvent
->AsPointerEvent()->pointerId
;
164 int32_t PointerEvent::Width() {
165 return ShouldResistFingerprinting() ? 1 : mEvent
->AsPointerEvent()->mWidth
;
168 int32_t PointerEvent::Height() {
169 return ShouldResistFingerprinting() ? 1 : mEvent
->AsPointerEvent()->mHeight
;
172 float PointerEvent::Pressure() {
173 if (mEvent
->mMessage
== ePointerUp
|| !ShouldResistFingerprinting()) {
174 return mEvent
->AsPointerEvent()->mPressure
;
177 // According to [1], we should use 0.5 when it is in active buttons state and
178 // 0 otherwise for devices that don't support pressure. And a pointerup event
179 // always reports 0, so we don't need to spoof that.
181 // [1] https://www.w3.org/TR/pointerevents/#dom-pointerevent-pressure
182 float spoofedPressure
= 0.0;
183 if (mEvent
->AsPointerEvent()->mButtons
) {
184 spoofedPressure
= 0.5;
187 return spoofedPressure
;
190 float PointerEvent::TangentialPressure() {
191 return ShouldResistFingerprinting()
193 : mEvent
->AsPointerEvent()->tangentialPressure
;
196 int32_t PointerEvent::TiltX() {
197 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->tiltX
;
200 int32_t PointerEvent::TiltY() {
201 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->tiltY
;
204 int32_t PointerEvent::Twist() {
205 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->twist
;
208 bool PointerEvent::IsPrimary() { return mEvent
->AsPointerEvent()->mIsPrimary
; }
210 bool PointerEvent::EnableGetCoalescedEvents(JSContext
* aCx
, JSObject
* aGlobal
) {
211 return !StaticPrefs::
212 dom_w3c_pointer_events_getcoalescedevents_only_in_securecontext() ||
213 nsContentUtils::IsSecureContextOrWebExtension(aCx
, aGlobal
);
216 void PointerEvent::GetCoalescedEvents(
217 nsTArray
<RefPtr
<PointerEvent
>>& aPointerEvents
) {
218 WidgetPointerEvent
* widgetEvent
= mEvent
->AsPointerEvent();
219 if (mCoalescedEvents
.IsEmpty() && widgetEvent
&&
220 widgetEvent
->mCoalescedWidgetEvents
&&
221 !widgetEvent
->mCoalescedWidgetEvents
->mEvents
.IsEmpty()) {
222 nsCOMPtr
<EventTarget
> owner
= do_QueryInterface(mOwner
);
223 for (WidgetPointerEvent
& event
:
224 widgetEvent
->mCoalescedWidgetEvents
->mEvents
) {
225 RefPtr
<PointerEvent
> domEvent
=
226 NS_NewDOMPointerEvent(owner
, nullptr, &event
);
228 // The dom event is derived from an OS generated widget event. Setup
229 // mWidget and mPresContext since they are necessary to calculate
230 // offsetX / offsetY.
231 domEvent
->mEvent
->AsGUIEvent()->mWidget
= widgetEvent
->mWidget
;
232 domEvent
->mPresContext
= mPresContext
;
234 // The coalesced widget mouse events shouldn't have been dispatched.
235 MOZ_ASSERT(!domEvent
->mEvent
->mTarget
);
236 // The event target should be the same as the dispatched event's target.
237 domEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
239 // JS could hold reference to dom events. We have to ask dom event to
240 // duplicate its private data to avoid the widget event is destroyed.
241 domEvent
->DuplicatePrivateData();
243 // Setup mPresContext again after DuplicatePrivateData since it clears
245 domEvent
->mPresContext
= mPresContext
;
246 mCoalescedEvents
.AppendElement(domEvent
);
249 if (mEvent
->IsTrusted() && mEvent
->mTarget
) {
250 for (RefPtr
<PointerEvent
>& pointerEvent
: mCoalescedEvents
) {
251 // Only set event target when it's null.
252 if (!pointerEvent
->mEvent
->mTarget
) {
253 pointerEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
257 aPointerEvents
.AppendElements(mCoalescedEvents
);
260 void PointerEvent::GetPredictedEvents(
261 nsTArray
<RefPtr
<PointerEvent
>>& aPointerEvents
) {
262 // XXX Add support for native predicted events, bug 1550461
263 if (mEvent
->IsTrusted() && mEvent
->mTarget
) {
264 for (RefPtr
<PointerEvent
>& pointerEvent
: mPredictedEvents
) {
265 // Only set event target when it's null.
266 if (!pointerEvent
->mEvent
->mTarget
) {
267 pointerEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
271 aPointerEvents
.AppendElements(mPredictedEvents
);
274 bool PointerEvent::ShouldResistFingerprinting() {
275 // There are three simple situations we don't need to spoof this pointer
277 // 1. The pref privcy.resistFingerprinting' is false, we fast return here
278 // since we don't need to do any QI of following codes.
279 // 2. This event is generated by scripts.
280 // 3. This event is a mouse pointer event.
281 // We don't need to check for the system group since pointer events won't be
282 // dispatched to the system group.
283 if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
284 RFPTarget::PointerEvents
) ||
285 !mEvent
->IsTrusted() ||
286 mEvent
->AsPointerEvent()->mInputSource
==
287 MouseEvent_Binding::MOZ_SOURCE_MOUSE
) {
291 // Pref is checked above, so use true as fallback.
292 nsCOMPtr
<Document
> doc
= GetDocument();
293 return doc
? doc
->ShouldResistFingerprinting(RFPTarget::PointerEvents
) : true;
296 } // namespace mozilla::dom
298 using namespace mozilla
;
299 using namespace mozilla::dom
;
301 already_AddRefed
<PointerEvent
> NS_NewDOMPointerEvent(
302 EventTarget
* aOwner
, nsPresContext
* aPresContext
,
303 WidgetPointerEvent
* aEvent
) {
304 RefPtr
<PointerEvent
> it
= new PointerEvent(aOwner
, aPresContext
, aEvent
);