Bug 1905259 - Add updated thumbs up and down icons, add CSS animation to active state...
[gecko.git] / dom / events / PointerEvent.cpp
bloba2d18d05534651b946537c015f6a9a5e150dc5c5
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"
16 #include "prtime.h"
17 #include "jsfriendapi.h"
19 namespace mozilla::dom {
21 PointerEvent::PointerEvent(EventTarget* aOwner, nsPresContext* aPresContext,
22 WidgetPointerEvent* aEvent)
23 : MouseEvent(aOwner, aPresContext,
24 aEvent ? aEvent
25 : new WidgetPointerEvent(false, eVoidEvent, nullptr)) {
26 NS_ASSERTION(mEvent->mClass == ePointerEventClass,
27 "event type mismatch ePointerEventClass");
29 WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
30 if (aEvent) {
31 mEventIsInternal = false;
32 } else {
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".
40 mDetail =
41 IsPointerEventMessageOriginallyMouseEventMessage(mouseEvent->mMessage)
42 ? mouseEvent->mClickCount
43 : 0;
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");
88 break;
89 case MouseEvent_Binding::MOZ_SOURCE_PEN:
90 aPointerTypeDest.AssignLiteral("pen");
91 break;
92 case MouseEvent_Binding::MOZ_SOURCE_TOUCH:
93 aPointerTypeDest.AssignLiteral("touch");
94 break;
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();
102 break;
103 default:
104 aPointerTypeDest.Truncate();
105 break;
109 // static
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);
145 return e.forget();
148 // static
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();
177 return;
180 if (ShouldResistFingerprinting()) {
181 aPointerType.AssignLiteral("mouse");
182 return;
185 ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource,
186 aPointerType);
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
275 // mPresContext.
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
307 // event.
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) {
319 return false;
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);
336 return it.forget();