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
38 // be 0. However, UI Events defines that it should be click count if the
39 // event type is "click".
41 IsPointerEventMessageOriginallyMouseEventMessage(mouseEvent
->mMessage
)
42 ? mouseEvent
->mClickCount
46 JSObject
* PointerEvent::WrapObjectInternal(JSContext
* aCx
,
47 JS::Handle
<JSObject
*> aGivenProto
) {
48 return PointerEvent_Binding::Wrap(aCx
, this, aGivenProto
);
51 static uint16_t ConvertStringToPointerType(const nsAString
& aPointerTypeArg
,
52 bool aForTrustedEvent
) {
53 if (aPointerTypeArg
.EqualsLiteral("mouse")) {
54 return MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
56 if (aPointerTypeArg
.EqualsLiteral("pen")) {
57 return MouseEvent_Binding::MOZ_SOURCE_PEN
;
59 if (aPointerTypeArg
.EqualsLiteral("touch")) {
60 return MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
63 // Some chrome script need to copy the input source of a source event to
64 // dispatching new event. Therefore, we need to allow chrome script to
65 // set it to any input sources which we are supporting. However, these
66 // types are not standardized by the specs. Therefore, we should do this
67 // only when the event is a trusted one.
68 if (aForTrustedEvent
) {
69 if (aPointerTypeArg
.EqualsLiteral("eraser")) {
70 return MouseEvent_Binding::MOZ_SOURCE_ERASER
;
72 if (aPointerTypeArg
.EqualsLiteral("cursor")) {
73 return MouseEvent_Binding::MOZ_SOURCE_CURSOR
;
75 if (aPointerTypeArg
.EqualsLiteral("keyboard")) {
76 return MouseEvent_Binding::MOZ_SOURCE_KEYBOARD
;
80 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
83 void ConvertPointerTypeToString(uint16_t aPointerTypeSrc
,
84 nsAString
& aPointerTypeDest
) {
85 switch (aPointerTypeSrc
) {
86 case MouseEvent_Binding::MOZ_SOURCE_MOUSE
:
87 aPointerTypeDest
.AssignLiteral("mouse");
89 case MouseEvent_Binding::MOZ_SOURCE_PEN
:
90 aPointerTypeDest
.AssignLiteral("pen");
92 case MouseEvent_Binding::MOZ_SOURCE_TOUCH
:
93 aPointerTypeDest
.AssignLiteral("touch");
95 // In ConvertStringToPointerType(), we allow chrome script to set the
96 // input source from Gecko specific pointerType value. However, we won't
97 // expose them to the web because they are not standardized.
98 case MouseEvent_Binding::MOZ_SOURCE_ERASER
:
99 case MouseEvent_Binding::MOZ_SOURCE_CURSOR
:
100 case MouseEvent_Binding::MOZ_SOURCE_KEYBOARD
:
101 aPointerTypeDest
.Truncate();
104 aPointerTypeDest
.Truncate();
110 already_AddRefed
<PointerEvent
> PointerEvent::Constructor(
111 EventTarget
* aOwner
, const nsAString
& aType
,
112 const PointerEventInit
& aParam
) {
113 RefPtr
<PointerEvent
> e
= new PointerEvent(aOwner
, nullptr, nullptr);
114 bool trusted
= e
->Init(aOwner
);
116 e
->InitMouseEvent(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
117 aParam
.mDetail
, aParam
.mScreenX
, aParam
.mScreenY
,
118 aParam
.mClientX
, aParam
.mClientY
, false, false, false,
119 false, aParam
.mButton
, aParam
.mRelatedTarget
);
120 e
->InitializeExtraMouseEventDictionaryMembers(aParam
);
121 e
->mPointerType
= Some(aParam
.mPointerType
);
123 WidgetPointerEvent
* widgetEvent
= e
->mEvent
->AsPointerEvent();
124 widgetEvent
->pointerId
= aParam
.mPointerId
;
125 widgetEvent
->mWidth
= aParam
.mWidth
;
126 widgetEvent
->mHeight
= aParam
.mHeight
;
127 widgetEvent
->mPressure
= aParam
.mPressure
;
128 widgetEvent
->tangentialPressure
= aParam
.mTangentialPressure
;
129 widgetEvent
->tiltX
= aParam
.mTiltX
;
130 widgetEvent
->tiltY
= aParam
.mTiltY
;
131 widgetEvent
->twist
= aParam
.mTwist
;
132 widgetEvent
->mInputSource
=
133 ConvertStringToPointerType(aParam
.mPointerType
, trusted
);
134 widgetEvent
->mIsPrimary
= aParam
.mIsPrimary
;
135 widgetEvent
->mButtons
= aParam
.mButtons
;
137 if (!aParam
.mCoalescedEvents
.IsEmpty()) {
138 e
->mCoalescedEvents
.AppendElements(aParam
.mCoalescedEvents
);
140 if (!aParam
.mPredictedEvents
.IsEmpty()) {
141 e
->mPredictedEvents
.AppendElements(aParam
.mPredictedEvents
);
143 e
->SetTrusted(trusted
);
144 e
->SetComposed(aParam
.mComposed
);
149 already_AddRefed
<PointerEvent
> PointerEvent::Constructor(
150 const GlobalObject
& aGlobal
, const nsAString
& aType
,
151 const PointerEventInit
& aParam
) {
152 nsCOMPtr
<EventTarget
> owner
= do_QueryInterface(aGlobal
.GetAsSupports());
153 return Constructor(owner
, aType
, aParam
);
156 NS_IMPL_CYCLE_COLLECTION_CLASS(PointerEvent
)
158 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PointerEvent
, MouseEvent
)
159 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCoalescedEvents
)
160 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPredictedEvents
)
161 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PointerEvent
, MouseEvent
)
164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCoalescedEvents
)
165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPredictedEvents
)
166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
168 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PointerEvent
)
169 NS_INTERFACE_MAP_END_INHERITING(MouseEvent
)
171 NS_IMPL_ADDREF_INHERITED(PointerEvent
, MouseEvent
)
172 NS_IMPL_RELEASE_INHERITED(PointerEvent
, MouseEvent
)
174 void PointerEvent::GetPointerType(nsAString
& aPointerType
) {
175 if (mPointerType
.isSome()) {
176 aPointerType
= mPointerType
.value();
180 if (ShouldResistFingerprinting()) {
181 aPointerType
.AssignLiteral("mouse");
185 ConvertPointerTypeToString(mEvent
->AsPointerEvent()->mInputSource
,
189 int32_t PointerEvent::PointerId() {
190 return ShouldResistFingerprinting()
191 ? PointerEventHandler::GetSpoofedPointerIdForRFP()
192 : mEvent
->AsPointerEvent()->pointerId
;
195 int32_t PointerEvent::Width() {
196 return ShouldResistFingerprinting() ? 1 : mEvent
->AsPointerEvent()->mWidth
;
199 int32_t PointerEvent::Height() {
200 return ShouldResistFingerprinting() ? 1 : mEvent
->AsPointerEvent()->mHeight
;
203 float PointerEvent::Pressure() {
204 if (mEvent
->mMessage
== ePointerUp
|| !ShouldResistFingerprinting()) {
205 return mEvent
->AsPointerEvent()->mPressure
;
208 // According to [1], we should use 0.5 when it is in active buttons state and
209 // 0 otherwise for devices that don't support pressure. And a pointerup event
210 // always reports 0, so we don't need to spoof that.
212 // [1] https://www.w3.org/TR/pointerevents/#dom-pointerevent-pressure
213 float spoofedPressure
= 0.0;
214 if (mEvent
->AsPointerEvent()->mButtons
) {
215 spoofedPressure
= 0.5;
218 return spoofedPressure
;
221 float PointerEvent::TangentialPressure() {
222 return ShouldResistFingerprinting()
224 : mEvent
->AsPointerEvent()->tangentialPressure
;
227 int32_t PointerEvent::TiltX() {
228 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->tiltX
;
231 int32_t PointerEvent::TiltY() {
232 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->tiltY
;
235 int32_t PointerEvent::Twist() {
236 return ShouldResistFingerprinting() ? 0 : mEvent
->AsPointerEvent()->twist
;
239 bool PointerEvent::IsPrimary() { return mEvent
->AsPointerEvent()->mIsPrimary
; }
241 bool PointerEvent::EnableGetCoalescedEvents(JSContext
* aCx
, JSObject
* aGlobal
) {
242 return !StaticPrefs::
243 dom_w3c_pointer_events_getcoalescedevents_only_in_securecontext() ||
244 nsContentUtils::IsSecureContextOrWebExtension(aCx
, aGlobal
);
247 void PointerEvent::GetCoalescedEvents(
248 nsTArray
<RefPtr
<PointerEvent
>>& aPointerEvents
) {
249 WidgetPointerEvent
* widgetEvent
= mEvent
->AsPointerEvent();
250 if (mCoalescedEvents
.IsEmpty() && widgetEvent
&&
251 widgetEvent
->mCoalescedWidgetEvents
&&
252 !widgetEvent
->mCoalescedWidgetEvents
->mEvents
.IsEmpty()) {
253 nsCOMPtr
<EventTarget
> owner
= do_QueryInterface(mOwner
);
254 for (WidgetPointerEvent
& event
:
255 widgetEvent
->mCoalescedWidgetEvents
->mEvents
) {
256 RefPtr
<PointerEvent
> domEvent
=
257 NS_NewDOMPointerEvent(owner
, nullptr, &event
);
259 // The dom event is derived from an OS generated widget event. Setup
260 // mWidget and mPresContext since they are necessary to calculate
261 // offsetX / offsetY.
262 domEvent
->mEvent
->AsGUIEvent()->mWidget
= widgetEvent
->mWidget
;
263 domEvent
->mPresContext
= mPresContext
;
265 // The coalesced widget mouse events shouldn't have been dispatched.
266 MOZ_ASSERT(!domEvent
->mEvent
->mTarget
);
267 // The event target should be the same as the dispatched event's target.
268 domEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
270 // JS could hold reference to dom events. We have to ask dom event to
271 // duplicate its private data to avoid the widget event is destroyed.
272 domEvent
->DuplicatePrivateData();
274 // Setup mPresContext again after DuplicatePrivateData since it clears
276 domEvent
->mPresContext
= mPresContext
;
277 mCoalescedEvents
.AppendElement(domEvent
);
280 if (mEvent
->IsTrusted() && mEvent
->mTarget
) {
281 for (RefPtr
<PointerEvent
>& pointerEvent
: mCoalescedEvents
) {
282 // Only set event target when it's null.
283 if (!pointerEvent
->mEvent
->mTarget
) {
284 pointerEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
288 aPointerEvents
.AppendElements(mCoalescedEvents
);
291 void PointerEvent::GetPredictedEvents(
292 nsTArray
<RefPtr
<PointerEvent
>>& aPointerEvents
) {
293 // XXX Add support for native predicted events, bug 1550461
294 if (mEvent
->IsTrusted() && mEvent
->mTarget
) {
295 for (RefPtr
<PointerEvent
>& pointerEvent
: mPredictedEvents
) {
296 // Only set event target when it's null.
297 if (!pointerEvent
->mEvent
->mTarget
) {
298 pointerEvent
->mEvent
->mTarget
= mEvent
->mTarget
;
302 aPointerEvents
.AppendElements(mPredictedEvents
);
305 bool PointerEvent::ShouldResistFingerprinting() {
306 // There are three simple situations we don't need to spoof this pointer
308 // 1. The pref privcy.resistFingerprinting' is false, we fast return here
309 // since we don't need to do any QI of following codes.
310 // 2. This event is generated by scripts.
311 // 3. This event is a mouse pointer event.
312 // We don't need to check for the system group since pointer events won't be
313 // dispatched to the system group.
314 if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
315 RFPTarget::PointerEvents
) ||
316 !mEvent
->IsTrusted() ||
317 mEvent
->AsPointerEvent()->mInputSource
==
318 MouseEvent_Binding::MOZ_SOURCE_MOUSE
) {
322 // Pref is checked above, so use true as fallback.
323 nsCOMPtr
<Document
> doc
= GetDocument();
324 return doc
? doc
->ShouldResistFingerprinting(RFPTarget::PointerEvents
) : true;
327 } // namespace mozilla::dom
329 using namespace mozilla
;
330 using namespace mozilla::dom
;
332 already_AddRefed
<PointerEvent
> NS_NewDOMPointerEvent(
333 EventTarget
* aOwner
, nsPresContext
* aPresContext
,
334 WidgetPointerEvent
* aEvent
) {
335 RefPtr
<PointerEvent
> it
= new PointerEvent(aOwner
, aPresContext
, aEvent
);