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 #include "mozilla/dom/Navigator.h"
8 #include "mozilla/dom/TouchEvent.h"
9 #include "mozilla/dom/Touch.h"
10 #include "mozilla/dom/TouchListBinding.h"
11 #include "mozilla/BasePrincipal.h"
12 #include "mozilla/LookAndFeel.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/TouchEvents.h"
16 #include "nsContentUtils.h"
17 #include "nsIDocShell.h"
18 #include "nsExceptionHandler.h"
20 namespace mozilla::dom
{
22 /******************************************************************************
24 *****************************************************************************/
26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList
)
27 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
28 NS_INTERFACE_MAP_ENTRY(nsISupports
)
31 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TouchList
, mParent
, mPoints
)
33 NS_IMPL_CYCLE_COLLECTING_ADDREF(TouchList
)
34 NS_IMPL_CYCLE_COLLECTING_RELEASE(TouchList
)
36 JSObject
* TouchList::WrapObject(JSContext
* aCx
,
37 JS::Handle
<JSObject
*> aGivenProto
) {
38 return TouchList_Binding::Wrap(aCx
, this, aGivenProto
);
42 bool TouchList::PrefEnabled(JSContext
* aCx
, JSObject
* aGlobal
) {
43 return TouchEvent::PrefEnabled(aCx
, aGlobal
);
46 /******************************************************************************
48 *****************************************************************************/
50 TouchEvent::TouchEvent(EventTarget
* aOwner
, nsPresContext
* aPresContext
,
51 WidgetTouchEvent
* aEvent
)
54 aEvent
? aEvent
: new WidgetTouchEvent(false, eVoidEvent
, nullptr)) {
56 mEventIsInternal
= false;
58 for (uint32_t i
= 0; i
< aEvent
->mTouches
.Length(); ++i
) {
59 Touch
* touch
= aEvent
->mTouches
[i
];
60 touch
->InitializePoints(mPresContext
, aEvent
);
63 mEventIsInternal
= true;
67 NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent
, UIEvent
,
68 mEvent
->AsTouchEvent()->mTouches
, mTouches
,
69 mTargetTouches
, mChangedTouches
)
71 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchEvent
)
72 NS_INTERFACE_MAP_END_INHERITING(UIEvent
)
74 NS_IMPL_ADDREF_INHERITED(TouchEvent
, UIEvent
)
75 NS_IMPL_RELEASE_INHERITED(TouchEvent
, UIEvent
)
77 void TouchEvent::InitTouchEvent(const nsAString
& aType
, bool aCanBubble
,
78 bool aCancelable
, nsGlobalWindowInner
* aView
,
79 int32_t aDetail
, bool aCtrlKey
, bool aAltKey
,
80 bool aShiftKey
, bool aMetaKey
,
81 TouchList
* aTouches
, TouchList
* aTargetTouches
,
82 TouchList
* aChangedTouches
) {
83 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
85 UIEvent::InitUIEvent(aType
, aCanBubble
, aCancelable
, aView
, aDetail
);
86 mEvent
->AsInputEvent()->InitBasicModifiers(aCtrlKey
, aAltKey
, aShiftKey
,
89 mEvent
->AsTouchEvent()->mTouches
.Clear();
91 // To support touch.target retargeting also when the event is
92 // created by JS, we need to copy Touch objects to the widget event.
93 // In order to not affect targetTouches, we don't check duplicates in that
95 mTargetTouches
= aTargetTouches
;
96 AssignTouchesToWidgetEvent(mTargetTouches
, false);
98 AssignTouchesToWidgetEvent(mTouches
, true);
99 mChangedTouches
= aChangedTouches
;
100 AssignTouchesToWidgetEvent(mChangedTouches
, true);
103 void TouchEvent::AssignTouchesToWidgetEvent(TouchList
* aList
,
104 bool aCheckDuplicates
) {
108 WidgetTouchEvent
* widgetTouchEvent
= mEvent
->AsTouchEvent();
109 for (uint32_t i
= 0; i
< aList
->Length(); ++i
) {
110 Touch
* touch
= aList
->Item(i
);
112 (!aCheckDuplicates
|| !widgetTouchEvent
->mTouches
.Contains(touch
))) {
113 widgetTouchEvent
->mTouches
.AppendElement(touch
);
118 TouchList
* TouchEvent::Touches() {
120 WidgetTouchEvent
* touchEvent
= mEvent
->AsTouchEvent();
121 if (mEvent
->mMessage
== eTouchEnd
|| mEvent
->mMessage
== eTouchCancel
) {
122 // for touchend events, remove any changed touches from mTouches
123 WidgetTouchEvent::AutoTouchArray unchangedTouches
;
124 const WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
125 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
126 if (!touches
[i
]->mChanged
) {
127 unchangedTouches
.AppendElement(touches
[i
]);
130 mTouches
= new TouchList(ToSupports(this), unchangedTouches
);
132 mTouches
= new TouchList(ToSupports(this), touchEvent
->mTouches
);
138 TouchList
* TouchEvent::TargetTouches() {
139 if (!mTargetTouches
|| !mTargetTouches
->Length()) {
140 WidgetTouchEvent
* touchEvent
= mEvent
->AsTouchEvent();
141 if (!mTargetTouches
) {
142 mTargetTouches
= new TouchList(ToSupports(this));
144 const WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
145 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
146 // for touchend/cancel events, don't append to the target list if this is
147 // a touch that is ending
148 if ((mEvent
->mMessage
!= eTouchEnd
&& mEvent
->mMessage
!= eTouchCancel
) ||
149 !touches
[i
]->mChanged
) {
150 bool equalTarget
= touches
[i
]->mTarget
== mEvent
->mTarget
;
152 // Need to still check if we're inside native anonymous content
153 // and the non-NAC target would be the same.
154 nsIContent
* touchTarget
=
155 nsIContent::FromEventTargetOrNull(touches
[i
]->mTarget
);
156 nsIContent
* eventTarget
=
157 nsIContent::FromEventTargetOrNull(mEvent
->mTarget
);
158 equalTarget
= touchTarget
&& eventTarget
&&
159 touchTarget
->FindFirstNonChromeOnlyAccessContent() ==
160 eventTarget
->FindFirstNonChromeOnlyAccessContent();
163 mTargetTouches
->Append(touches
[i
]);
168 return mTargetTouches
;
171 TouchList
* TouchEvent::ChangedTouches() {
172 if (!mChangedTouches
) {
173 WidgetTouchEvent::AutoTouchArray changedTouches
;
174 WidgetTouchEvent
* touchEvent
= mEvent
->AsTouchEvent();
175 const WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
176 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
177 if (touches
[i
]->mChanged
) {
178 changedTouches
.AppendElement(touches
[i
]);
181 mChangedTouches
= new TouchList(ToSupports(this), changedTouches
);
183 return mChangedTouches
;
187 bool TouchEvent::PrefEnabled(JSContext
* aCx
, JSObject
* aGlobal
) {
188 nsIDocShell
* docShell
= nullptr;
190 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aGlobal
);
192 docShell
= win
->GetDocShell();
195 return PrefEnabled(docShell
);
198 static bool PlatformSupportsTouch() {
199 // Touch events are only actually supported if APZ is enabled. If APZ is
200 // disabled globally, we can check that once and incorporate that into the
201 // cached state. If APZ is enabled, we need to further check based on the
202 // widget, which we do in PrefEnabled (and don't cache that result).
203 static bool sIsTouchDeviceSupportPresent
=
204 !!LookAndFeel::GetInt(LookAndFeel::IntID::TouchDeviceSupportPresent
) &&
205 gfxPlatform::AsyncPanZoomEnabled();
207 return sIsTouchDeviceSupportPresent
;
211 bool TouchEvent::PrefEnabled(nsIDocShell
* aDocShell
) {
212 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
214 auto touchEventsOverride
= mozilla::dom::TouchEventsOverride::None
;
216 if (BrowsingContext
* bc
= aDocShell
->GetBrowsingContext()) {
217 touchEventsOverride
= bc
->TouchEventsOverride();
221 bool enabled
= false;
222 if (touchEventsOverride
== mozilla::dom::TouchEventsOverride::Enabled
) {
224 } else if (touchEventsOverride
==
225 mozilla::dom::TouchEventsOverride::Disabled
) {
228 const int32_t prefValue
= StaticPrefs::dom_w3c_touch_events_enabled();
229 if (prefValue
== 2) {
230 enabled
= PlatformSupportsTouch();
232 static bool firstTime
= true;
233 // The touch screen data seems to be inaccurate in the parent process,
234 // and we really need the crash annotation in child processes.
235 if (firstTime
&& !XRE_IsParentProcess()) {
236 CrashReporter::RecordAnnotationBool(
237 CrashReporter::Annotation::HasDeviceTouchScreen
, enabled
);
241 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
242 if (enabled
&& aDocShell
) {
243 // APZ might be disabled on this particular widget, in which case
244 // TouchEvent support will also be disabled. Try to detect that.
245 RefPtr
<nsPresContext
> pc
= aDocShell
->GetPresContext();
247 nsCOMPtr
<nsIWidget
> widget
= pc
->GetRootWidget();
249 enabled
&= widget
->AsyncPanZoomEnabled();
255 enabled
= !!prefValue
;
260 nsContentUtils::InitializeTouchEventTable();
266 bool TouchEvent::LegacyAPIEnabled(JSContext
* aCx
, JSObject
* aGlobal
) {
267 nsIPrincipal
* principal
= nsContentUtils::SubjectPrincipal(aCx
);
268 bool isSystem
= principal
&& principal
->IsSystemPrincipal();
270 nsIDocShell
* docShell
= nullptr;
272 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aGlobal
);
274 docShell
= win
->GetDocShell();
277 return LegacyAPIEnabled(docShell
, isSystem
);
281 bool TouchEvent::LegacyAPIEnabled(nsIDocShell
* aDocShell
,
282 bool aCallerIsSystem
) {
283 return (aCallerIsSystem
||
284 StaticPrefs::dom_w3c_touch_events_legacy_apis_enabled() ||
285 (aDocShell
&& aDocShell
->GetBrowsingContext() &&
286 aDocShell
->GetBrowsingContext()->TouchEventsOverride() ==
287 mozilla::dom::TouchEventsOverride::Enabled
)) &&
288 PrefEnabled(aDocShell
);
292 already_AddRefed
<TouchEvent
> TouchEvent::Constructor(
293 const GlobalObject
& aGlobal
, const nsAString
& aType
,
294 const TouchEventInit
& aParam
) {
295 nsCOMPtr
<EventTarget
> t
= do_QueryInterface(aGlobal
.GetAsSupports());
296 RefPtr
<TouchEvent
> e
= new TouchEvent(t
, nullptr, nullptr);
297 bool trusted
= e
->Init(t
);
298 RefPtr
<TouchList
> touches
= e
->CopyTouches(aParam
.mTouches
);
299 RefPtr
<TouchList
> targetTouches
= e
->CopyTouches(aParam
.mTargetTouches
);
300 RefPtr
<TouchList
> changedTouches
= e
->CopyTouches(aParam
.mChangedTouches
);
301 e
->InitTouchEvent(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
302 aParam
.mDetail
, aParam
.mCtrlKey
, aParam
.mAltKey
,
303 aParam
.mShiftKey
, aParam
.mMetaKey
, touches
, targetTouches
,
305 e
->SetTrusted(trusted
);
306 e
->SetComposed(aParam
.mComposed
);
310 already_AddRefed
<TouchList
> TouchEvent::CopyTouches(
311 const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
312 RefPtr
<TouchList
> list
= new TouchList(GetParentObject());
313 size_t len
= aTouches
.Length();
314 for (size_t i
= 0; i
< len
; ++i
) {
315 list
->Append(aTouches
[i
]);
317 return list
.forget();
320 bool TouchEvent::AltKey() { return mEvent
->AsTouchEvent()->IsAlt(); }
322 bool TouchEvent::MetaKey() { return mEvent
->AsTouchEvent()->IsMeta(); }
324 bool TouchEvent::CtrlKey() { return mEvent
->AsTouchEvent()->IsControl(); }
326 bool TouchEvent::ShiftKey() { return mEvent
->AsTouchEvent()->IsShift(); }
328 } // namespace mozilla::dom
330 using namespace mozilla
;
331 using namespace mozilla::dom
;
333 already_AddRefed
<TouchEvent
> NS_NewDOMTouchEvent(EventTarget
* aOwner
,
334 nsPresContext
* aPresContext
,
335 WidgetTouchEvent
* aEvent
) {
336 RefPtr
<TouchEvent
> it
= new TouchEvent(aOwner
, aPresContext
, aEvent
);