Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / events / TouchEvent.cpp
blob24068703be626f837ff7e114f94eca39bee2d405
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 /******************************************************************************
23 * TouchList
24 *****************************************************************************/
26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList)
27 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
28 NS_INTERFACE_MAP_ENTRY(nsISupports)
29 NS_INTERFACE_MAP_END
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);
41 // static
42 bool TouchList::PrefEnabled(JSContext* aCx, JSObject* aGlobal) {
43 return TouchEvent::PrefEnabled(aCx, aGlobal);
46 /******************************************************************************
47 * TouchEvent
48 *****************************************************************************/
50 TouchEvent::TouchEvent(EventTarget* aOwner, nsPresContext* aPresContext,
51 WidgetTouchEvent* aEvent)
52 : UIEvent(
53 aOwner, aPresContext,
54 aEvent ? aEvent : new WidgetTouchEvent(false, eVoidEvent, nullptr)) {
55 if (aEvent) {
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);
62 } else {
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,
87 aMetaKey);
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
94 // list.
95 mTargetTouches = aTargetTouches;
96 AssignTouchesToWidgetEvent(mTargetTouches, false);
97 mTouches = aTouches;
98 AssignTouchesToWidgetEvent(mTouches, true);
99 mChangedTouches = aChangedTouches;
100 AssignTouchesToWidgetEvent(mChangedTouches, true);
103 void TouchEvent::AssignTouchesToWidgetEvent(TouchList* aList,
104 bool aCheckDuplicates) {
105 if (!aList) {
106 return;
108 WidgetTouchEvent* widgetTouchEvent = mEvent->AsTouchEvent();
109 for (uint32_t i = 0; i < aList->Length(); ++i) {
110 Touch* touch = aList->Item(i);
111 if (touch &&
112 (!aCheckDuplicates || !widgetTouchEvent->mTouches.Contains(touch))) {
113 widgetTouchEvent->mTouches.AppendElement(touch);
118 TouchList* TouchEvent::Touches() {
119 if (!mTouches) {
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);
131 } else {
132 mTouches = new TouchList(ToSupports(this), touchEvent->mTouches);
135 return 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;
151 if (!equalTarget) {
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();
162 if (equalTarget) {
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;
186 // static
187 bool TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal) {
188 nsIDocShell* docShell = nullptr;
189 if (aGlobal) {
190 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
191 if (win) {
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;
210 // static
211 bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) {
212 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
214 auto touchEventsOverride = mozilla::dom::TouchEventsOverride::None;
215 if (aDocShell) {
216 if (BrowsingContext* bc = aDocShell->GetBrowsingContext()) {
217 touchEventsOverride = bc->TouchEventsOverride();
221 bool enabled = false;
222 if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Enabled) {
223 enabled = true;
224 } else if (touchEventsOverride ==
225 mozilla::dom::TouchEventsOverride::Disabled) {
226 enabled = false;
227 } else {
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::AnnotateCrashReport(
237 CrashReporter::Annotation::HasDeviceTouchScreen, enabled);
238 firstTime = false;
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();
246 if (pc) {
247 nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
248 if (widget) {
249 enabled &= widget->AsyncPanZoomEnabled();
253 #endif
254 } else {
255 enabled = !!prefValue;
259 if (enabled) {
260 nsContentUtils::InitializeTouchEventTable();
262 return enabled;
265 // static
266 bool TouchEvent::LegacyAPIEnabled(JSContext* aCx, JSObject* aGlobal) {
267 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
268 bool isSystem = principal && principal->IsSystemPrincipal();
270 nsIDocShell* docShell = nullptr;
271 if (aGlobal) {
272 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
273 if (win) {
274 docShell = win->GetDocShell();
277 return LegacyAPIEnabled(docShell, isSystem);
280 // static
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);
291 // static
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,
304 changedTouches);
305 e->SetTrusted(trusted);
306 e->SetComposed(aParam.mComposed);
307 return e.forget();
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);
337 return it.forget();