summaryrefslogtreecommitdiffstats
path: root/dom/events/PointerEvent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/PointerEvent.cpp')
-rw-r--r--dom/events/PointerEvent.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/dom/events/PointerEvent.cpp b/dom/events/PointerEvent.cpp
new file mode 100644
index 0000000000..eac8c54a75
--- /dev/null
+++ b/dom/events/PointerEvent.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Portions Copyright 2013 Microsoft Open Technologies, Inc. */
+
+#include "mozilla/dom/MouseEventBinding.h"
+#include "mozilla/dom/PointerEvent.h"
+#include "mozilla/dom/PointerEventBinding.h"
+#include "mozilla/dom/PointerEventHandler.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsContentUtils.h"
+#include "prtime.h"
+#include "jsfriendapi.h"
+
+namespace mozilla::dom {
+
+PointerEvent::PointerEvent(EventTarget* aOwner, nsPresContext* aPresContext,
+ WidgetPointerEvent* aEvent)
+ : MouseEvent(aOwner, aPresContext,
+ aEvent ? aEvent
+ : new WidgetPointerEvent(false, eVoidEvent, nullptr)) {
+ NS_ASSERTION(mEvent->mClass == ePointerEventClass,
+ "event type mismatch ePointerEventClass");
+
+ WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
+ if (aEvent) {
+ mEventIsInternal = false;
+ } else {
+ mEventIsInternal = true;
+ mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
+ mouseEvent->mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+ }
+ // 5.2 Pointer Event types, for all pointer events, |detail| attribute SHOULD
+ // be 0.
+ mDetail = 0;
+}
+
+JSObject* PointerEvent::WrapObjectInternal(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return PointerEvent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+static uint16_t ConvertStringToPointerType(const nsAString& aPointerTypeArg) {
+ if (aPointerTypeArg.EqualsLiteral("mouse")) {
+ return MouseEvent_Binding::MOZ_SOURCE_MOUSE;
+ }
+ if (aPointerTypeArg.EqualsLiteral("pen")) {
+ return MouseEvent_Binding::MOZ_SOURCE_PEN;
+ }
+ if (aPointerTypeArg.EqualsLiteral("touch")) {
+ return MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+ }
+
+ return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+}
+
+void ConvertPointerTypeToString(uint16_t aPointerTypeSrc,
+ nsAString& aPointerTypeDest) {
+ switch (aPointerTypeSrc) {
+ case MouseEvent_Binding::MOZ_SOURCE_MOUSE:
+ aPointerTypeDest.AssignLiteral("mouse");
+ break;
+ case MouseEvent_Binding::MOZ_SOURCE_PEN:
+ aPointerTypeDest.AssignLiteral("pen");
+ break;
+ case MouseEvent_Binding::MOZ_SOURCE_TOUCH:
+ aPointerTypeDest.AssignLiteral("touch");
+ break;
+ default:
+ aPointerTypeDest.Truncate();
+ break;
+ }
+}
+
+// static
+already_AddRefed<PointerEvent> PointerEvent::Constructor(
+ EventTarget* aOwner, const nsAString& aType,
+ const PointerEventInit& aParam) {
+ RefPtr<PointerEvent> e = new PointerEvent(aOwner, nullptr, nullptr);
+ bool trusted = e->Init(aOwner);
+
+ e->InitMouseEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
+ aParam.mDetail, aParam.mScreenX, aParam.mScreenY,
+ aParam.mClientX, aParam.mClientY, false, false, false,
+ false, aParam.mButton, aParam.mRelatedTarget);
+ e->InitializeExtraMouseEventDictionaryMembers(aParam);
+ e->mPointerType = Some(aParam.mPointerType);
+
+ WidgetPointerEvent* widgetEvent = e->mEvent->AsPointerEvent();
+ widgetEvent->pointerId = aParam.mPointerId;
+ widgetEvent->mWidth = aParam.mWidth;
+ widgetEvent->mHeight = aParam.mHeight;
+ widgetEvent->mPressure = aParam.mPressure;
+ widgetEvent->tangentialPressure = aParam.mTangentialPressure;
+ widgetEvent->tiltX = aParam.mTiltX;
+ widgetEvent->tiltY = aParam.mTiltY;
+ widgetEvent->twist = aParam.mTwist;
+ widgetEvent->mInputSource = ConvertStringToPointerType(aParam.mPointerType);
+ widgetEvent->mIsPrimary = aParam.mIsPrimary;
+ widgetEvent->mButtons = aParam.mButtons;
+
+ if (!aParam.mCoalescedEvents.IsEmpty()) {
+ e->mCoalescedEvents.AppendElements(aParam.mCoalescedEvents);
+ }
+ if (!aParam.mPredictedEvents.IsEmpty()) {
+ e->mPredictedEvents.AppendElements(aParam.mPredictedEvents);
+ }
+ e->SetTrusted(trusted);
+ e->SetComposed(aParam.mComposed);
+ return e.forget();
+}
+
+// static
+already_AddRefed<PointerEvent> PointerEvent::Constructor(
+ const GlobalObject& aGlobal, const nsAString& aType,
+ const PointerEventInit& aParam) {
+ nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(owner, aType, aParam);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(PointerEvent)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PointerEvent, MouseEvent)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCoalescedEvents)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPredictedEvents)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PointerEvent, MouseEvent)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCoalescedEvents)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPredictedEvents)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PointerEvent)
+NS_INTERFACE_MAP_END_INHERITING(MouseEvent)
+
+NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent)
+NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent)
+
+void PointerEvent::GetPointerType(nsAString& aPointerType) {
+ if (mPointerType.isSome()) {
+ aPointerType = mPointerType.value();
+ return;
+ }
+
+ if (ShouldResistFingerprinting()) {
+ aPointerType.AssignLiteral("mouse");
+ return;
+ }
+
+ ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource,
+ aPointerType);
+}
+
+int32_t PointerEvent::PointerId() {
+ return ShouldResistFingerprinting()
+ ? PointerEventHandler::GetSpoofedPointerIdForRFP()
+ : mEvent->AsPointerEvent()->pointerId;
+}
+
+int32_t PointerEvent::Width() {
+ return ShouldResistFingerprinting() ? 1 : mEvent->AsPointerEvent()->mWidth;
+}
+
+int32_t PointerEvent::Height() {
+ return ShouldResistFingerprinting() ? 1 : mEvent->AsPointerEvent()->mHeight;
+}
+
+float PointerEvent::Pressure() {
+ if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) {
+ return mEvent->AsPointerEvent()->mPressure;
+ }
+
+ // According to [1], we should use 0.5 when it is in active buttons state and
+ // 0 otherwise for devices that don't support pressure. And a pointerup event
+ // always reports 0, so we don't need to spoof that.
+ //
+ // [1] https://www.w3.org/TR/pointerevents/#dom-pointerevent-pressure
+ float spoofedPressure = 0.0;
+ if (mEvent->AsPointerEvent()->mButtons) {
+ spoofedPressure = 0.5;
+ }
+
+ return spoofedPressure;
+}
+
+float PointerEvent::TangentialPressure() {
+ return ShouldResistFingerprinting()
+ ? 0
+ : mEvent->AsPointerEvent()->tangentialPressure;
+}
+
+int32_t PointerEvent::TiltX() {
+ return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->tiltX;
+}
+
+int32_t PointerEvent::TiltY() {
+ return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->tiltY;
+}
+
+int32_t PointerEvent::Twist() {
+ return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist;
+}
+
+bool PointerEvent::IsPrimary() { return mEvent->AsPointerEvent()->mIsPrimary; }
+
+bool PointerEvent::EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal) {
+ return !StaticPrefs::
+ dom_w3c_pointer_events_getcoalescedevents_only_in_securecontext() ||
+ nsContentUtils::IsSecureContextOrWebExtension(aCx, aGlobal);
+}
+
+void PointerEvent::GetCoalescedEvents(
+ nsTArray<RefPtr<PointerEvent>>& aPointerEvents) {
+ WidgetPointerEvent* widgetEvent = mEvent->AsPointerEvent();
+ if (mCoalescedEvents.IsEmpty() && widgetEvent &&
+ widgetEvent->mCoalescedWidgetEvents &&
+ !widgetEvent->mCoalescedWidgetEvents->mEvents.IsEmpty()) {
+ nsCOMPtr<EventTarget> owner = do_QueryInterface(mOwner);
+ for (WidgetPointerEvent& event :
+ widgetEvent->mCoalescedWidgetEvents->mEvents) {
+ RefPtr<PointerEvent> domEvent =
+ NS_NewDOMPointerEvent(owner, nullptr, &event);
+
+ // The dom event is derived from an OS generated widget event. Setup
+ // mWidget and mPresContext since they are necessary to calculate
+ // offsetX / offsetY.
+ domEvent->mEvent->AsGUIEvent()->mWidget = widgetEvent->mWidget;
+ domEvent->mPresContext = mPresContext;
+
+ // The coalesced widget mouse events shouldn't have been dispatched.
+ MOZ_ASSERT(!domEvent->mEvent->mTarget);
+ // The event target should be the same as the dispatched event's target.
+ domEvent->mEvent->mTarget = mEvent->mTarget;
+
+ // JS could hold reference to dom events. We have to ask dom event to
+ // duplicate its private data to avoid the widget event is destroyed.
+ domEvent->DuplicatePrivateData();
+
+ // Setup mPresContext again after DuplicatePrivateData since it clears
+ // mPresContext.
+ domEvent->mPresContext = mPresContext;
+ mCoalescedEvents.AppendElement(domEvent);
+ }
+ }
+ if (mEvent->IsTrusted() && mEvent->mTarget) {
+ for (RefPtr<PointerEvent>& pointerEvent : mCoalescedEvents) {
+ // Only set event target when it's null.
+ if (!pointerEvent->mEvent->mTarget) {
+ pointerEvent->mEvent->mTarget = mEvent->mTarget;
+ }
+ }
+ }
+ aPointerEvents.AppendElements(mCoalescedEvents);
+}
+
+void PointerEvent::GetPredictedEvents(
+ nsTArray<RefPtr<PointerEvent>>& aPointerEvents) {
+ // XXX Add support for native predicted events, bug 1550461
+ if (mEvent->IsTrusted() && mEvent->mTarget) {
+ for (RefPtr<PointerEvent>& pointerEvent : mPredictedEvents) {
+ // Only set event target when it's null.
+ if (!pointerEvent->mEvent->mTarget) {
+ pointerEvent->mEvent->mTarget = mEvent->mTarget;
+ }
+ }
+ }
+ aPointerEvents.AppendElements(mPredictedEvents);
+}
+
+bool PointerEvent::ShouldResistFingerprinting() {
+ // There are three simple situations we don't need to spoof this pointer
+ // event.
+ // 1. The pref privcy.resistFingerprinting' is false, we fast return here
+ // since we don't need to do any QI of following codes.
+ // 2. This event is generated by scripts.
+ // 3. This event is a mouse pointer event.
+ // We don't need to check for the system group since pointer events won't be
+ // dispatched to the system group.
+ if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
+ RFPTarget::PointerEvents) ||
+ !mEvent->IsTrusted() ||
+ mEvent->AsPointerEvent()->mInputSource ==
+ MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
+ return false;
+ }
+
+ // Pref is checked above, so use true as fallback.
+ nsCOMPtr<Document> doc = GetDocument();
+ return doc ? doc->ShouldResistFingerprinting(RFPTarget::PointerEvents) : true;
+}
+
+} // namespace mozilla::dom
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+already_AddRefed<PointerEvent> NS_NewDOMPointerEvent(
+ EventTarget* aOwner, nsPresContext* aPresContext,
+ WidgetPointerEvent* aEvent) {
+ RefPtr<PointerEvent> it = new PointerEvent(aOwner, aPresContext, aEvent);
+ return it.forget();
+}