Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / events / PointerEvent.cpp
blobeac8c54a758c6cec29e0a144c3d5dd8c306652c7
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.
39 mDetail = 0;
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");
66 break;
67 case MouseEvent_Binding::MOZ_SOURCE_PEN:
68 aPointerTypeDest.AssignLiteral("pen");
69 break;
70 case MouseEvent_Binding::MOZ_SOURCE_TOUCH:
71 aPointerTypeDest.AssignLiteral("touch");
72 break;
73 default:
74 aPointerTypeDest.Truncate();
75 break;
79 // static
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);
114 return e.forget();
117 // static
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();
146 return;
149 if (ShouldResistFingerprinting()) {
150 aPointerType.AssignLiteral("mouse");
151 return;
154 ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource,
155 aPointerType);
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
244 // mPresContext.
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
276 // event.
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) {
288 return false;
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);
305 return it.forget();