/src/mozilla-central/dom/events/TouchEvent.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
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/Preferences.h" |
12 | | #include "mozilla/TouchEvents.h" |
13 | | #include "nsContentUtils.h" |
14 | | #include "nsIDocShell.h" |
15 | | #include "mozilla/WidgetUtils.h" |
16 | | |
17 | | namespace mozilla { |
18 | | |
19 | | namespace dom { |
20 | | |
21 | | /****************************************************************************** |
22 | | * TouchList |
23 | | *****************************************************************************/ |
24 | | |
25 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList) |
26 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
27 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
28 | 0 | NS_INTERFACE_MAP_END |
29 | | |
30 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TouchList, mParent, mPoints) |
31 | | |
32 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(TouchList) |
33 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(TouchList) |
34 | | |
35 | | JSObject* |
36 | | TouchList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
37 | 0 | { |
38 | 0 | return TouchList_Binding::Wrap(aCx, this, aGivenProto); |
39 | 0 | } |
40 | | |
41 | | // static |
42 | | bool |
43 | | TouchList::PrefEnabled(JSContext* aCx, JSObject* aGlobal) |
44 | 0 | { |
45 | 0 | return TouchEvent::PrefEnabled(aCx, aGlobal); |
46 | 0 | } |
47 | | |
48 | | /****************************************************************************** |
49 | | * TouchEvent |
50 | | *****************************************************************************/ |
51 | | |
52 | | TouchEvent::TouchEvent(EventTarget* aOwner, |
53 | | nsPresContext* aPresContext, |
54 | | WidgetTouchEvent* aEvent) |
55 | | : UIEvent(aOwner, aPresContext, |
56 | | aEvent ? aEvent : |
57 | | new WidgetTouchEvent(false, eVoidEvent, nullptr)) |
58 | 0 | { |
59 | 0 | if (aEvent) { |
60 | 0 | mEventIsInternal = false; |
61 | 0 |
|
62 | 0 | for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) { |
63 | 0 | Touch* touch = aEvent->mTouches[i]; |
64 | 0 | touch->InitializePoints(mPresContext, aEvent); |
65 | 0 | } |
66 | 0 | } else { |
67 | 0 | mEventIsInternal = true; |
68 | 0 | mEvent->mTime = PR_Now(); |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | | NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent, UIEvent, |
73 | | mEvent->AsTouchEvent()->mTouches, |
74 | | mTouches, |
75 | | mTargetTouches, |
76 | | mChangedTouches) |
77 | | |
78 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchEvent) |
79 | 0 | NS_INTERFACE_MAP_END_INHERITING(UIEvent) |
80 | | |
81 | | NS_IMPL_ADDREF_INHERITED(TouchEvent, UIEvent) |
82 | | NS_IMPL_RELEASE_INHERITED(TouchEvent, UIEvent) |
83 | | |
84 | | void |
85 | | TouchEvent::InitTouchEvent(const nsAString& aType, |
86 | | bool aCanBubble, |
87 | | bool aCancelable, |
88 | | nsGlobalWindowInner* aView, |
89 | | int32_t aDetail, |
90 | | bool aCtrlKey, |
91 | | bool aAltKey, |
92 | | bool aShiftKey, |
93 | | bool aMetaKey, |
94 | | TouchList* aTouches, |
95 | | TouchList* aTargetTouches, |
96 | | TouchList* aChangedTouches) |
97 | 0 | { |
98 | 0 | NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched); |
99 | 0 |
|
100 | 0 | UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail); |
101 | 0 | mEvent->AsInputEvent()->InitBasicModifiers(aCtrlKey, aAltKey, |
102 | 0 | aShiftKey, aMetaKey); |
103 | 0 |
|
104 | 0 | mEvent->AsTouchEvent()->mTouches.Clear(); |
105 | 0 |
|
106 | 0 | // To support touch.target retargeting also when the event is |
107 | 0 | // created by JS, we need to copy Touch objects to the widget event. |
108 | 0 | // In order to not affect targetTouches, we don't check duplicates in that |
109 | 0 | // list. |
110 | 0 | mTargetTouches = aTargetTouches; |
111 | 0 | AssignTouchesToWidgetEvent(mTargetTouches, false); |
112 | 0 | mTouches = aTouches; |
113 | 0 | AssignTouchesToWidgetEvent(mTouches, true); |
114 | 0 | mChangedTouches = aChangedTouches; |
115 | 0 | AssignTouchesToWidgetEvent(mChangedTouches, true); |
116 | 0 | } |
117 | | |
118 | | void |
119 | | TouchEvent::AssignTouchesToWidgetEvent(TouchList* aList, bool aCheckDuplicates) |
120 | 0 | { |
121 | 0 | if (!aList) { |
122 | 0 | return; |
123 | 0 | } |
124 | 0 | WidgetTouchEvent* widgetTouchEvent = mEvent->AsTouchEvent(); |
125 | 0 | for (uint32_t i = 0; i < aList->Length(); ++i) { |
126 | 0 | Touch* touch = aList->Item(i); |
127 | 0 | if (touch && |
128 | 0 | (!aCheckDuplicates || |
129 | 0 | !widgetTouchEvent->mTouches.Contains(touch))) { |
130 | 0 | widgetTouchEvent->mTouches.AppendElement(touch); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | TouchList* |
136 | | TouchEvent::Touches() |
137 | 0 | { |
138 | 0 | if (!mTouches) { |
139 | 0 | WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); |
140 | 0 | if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) { |
141 | 0 | // for touchend events, remove any changed touches from mTouches |
142 | 0 | WidgetTouchEvent::AutoTouchArray unchangedTouches; |
143 | 0 | const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
144 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
145 | 0 | if (!touches[i]->mChanged) { |
146 | 0 | unchangedTouches.AppendElement(touches[i]); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | mTouches = new TouchList(ToSupports(this), unchangedTouches); |
150 | 0 | } else { |
151 | 0 | mTouches = new TouchList(ToSupports(this), touchEvent->mTouches); |
152 | 0 | } |
153 | 0 | } |
154 | 0 | return mTouches; |
155 | 0 | } |
156 | | |
157 | | TouchList* |
158 | | TouchEvent::TargetTouches() |
159 | 0 | { |
160 | 0 | if (!mTargetTouches || !mTargetTouches->Length()) { |
161 | 0 | WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); |
162 | 0 | if (!mTargetTouches) { |
163 | 0 | mTargetTouches = new TouchList(ToSupports(this)); |
164 | 0 | } |
165 | 0 | const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
166 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
167 | 0 | // for touchend/cancel events, don't append to the target list if this is a |
168 | 0 | // touch that is ending |
169 | 0 | if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) || |
170 | 0 | !touches[i]->mChanged) { |
171 | 0 | bool equalTarget = touches[i]->mTarget == mEvent->mTarget; |
172 | 0 | if (!equalTarget) { |
173 | 0 | // Need to still check if we're inside native anonymous content |
174 | 0 | // and the non-NAC target would be the same. |
175 | 0 | nsCOMPtr<nsIContent> touchTarget = |
176 | 0 | do_QueryInterface(touches[i]->mTarget); |
177 | 0 | nsCOMPtr<nsIContent> eventTarget = |
178 | 0 | do_QueryInterface(mEvent->mTarget); |
179 | 0 | equalTarget = touchTarget && eventTarget && |
180 | 0 | touchTarget->FindFirstNonChromeOnlyAccessContent() == |
181 | 0 | eventTarget->FindFirstNonChromeOnlyAccessContent(); |
182 | 0 | } |
183 | 0 | if (equalTarget) { |
184 | 0 | mTargetTouches->Append(touches[i]); |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | 0 | } |
189 | 0 | return mTargetTouches; |
190 | 0 | } |
191 | | |
192 | | TouchList* |
193 | | TouchEvent::ChangedTouches() |
194 | 0 | { |
195 | 0 | if (!mChangedTouches) { |
196 | 0 | WidgetTouchEvent::AutoTouchArray changedTouches; |
197 | 0 | WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); |
198 | 0 | const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
199 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
200 | 0 | if (touches[i]->mChanged) { |
201 | 0 | changedTouches.AppendElement(touches[i]); |
202 | 0 | } |
203 | 0 | } |
204 | 0 | mChangedTouches = new TouchList(ToSupports(this), changedTouches); |
205 | 0 | } |
206 | 0 | return mChangedTouches; |
207 | 0 | } |
208 | | |
209 | | // static |
210 | | bool |
211 | | TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal) |
212 | 0 | { |
213 | 0 | nsIDocShell* docShell = nullptr; |
214 | 0 | if (aGlobal) { |
215 | 0 | nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal); |
216 | 0 | if (win) { |
217 | 0 | docShell = win->GetDocShell(); |
218 | 0 | } |
219 | 0 | } |
220 | 0 | return PrefEnabled(docShell); |
221 | 0 | } |
222 | | |
223 | | // static |
224 | | bool |
225 | | TouchEvent::PlatformSupportsTouch() |
226 | 0 | { |
227 | | #if defined(MOZ_WIDGET_ANDROID) |
228 | | // Touch support is always enabled on android. |
229 | | return true; |
230 | | #elif defined(XP_WIN) || defined(MOZ_WIDGET_GTK) |
231 | | static bool sDidCheckTouchDeviceSupport = false; |
232 | 0 | static bool sIsTouchDeviceSupportPresent = false; |
233 | 0 | // On Windows and GTK3 we auto-detect based on device support. |
234 | 0 | if (!sDidCheckTouchDeviceSupport) { |
235 | 0 | sDidCheckTouchDeviceSupport = true; |
236 | 0 | sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent(); |
237 | 0 | // But touch events are only actually supported if APZ is enabled. If |
238 | 0 | // APZ is disabled globally, we can check that once and incorporate that |
239 | 0 | // into the cached state. If APZ is enabled, we need to further check |
240 | 0 | // based on the widget, which we do below (and don't cache that result). |
241 | 0 | sIsTouchDeviceSupportPresent &= gfxPlatform::AsyncPanZoomEnabled(); |
242 | 0 | } |
243 | 0 | return sIsTouchDeviceSupportPresent; |
244 | | #else |
245 | | return false; |
246 | | #endif |
247 | | } |
248 | | |
249 | | // static |
250 | | bool |
251 | | TouchEvent::PrefEnabled(nsIDocShell* aDocShell) |
252 | 0 | { |
253 | 0 | static bool sPrefCached = false; |
254 | 0 | static int32_t sPrefCacheValue = 0; |
255 | 0 |
|
256 | 0 | uint32_t touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE; |
257 | 0 | if (aDocShell) { |
258 | 0 | aDocShell->GetTouchEventsOverride(&touchEventsOverride); |
259 | 0 | } |
260 | 0 |
|
261 | 0 | if (!sPrefCached) { |
262 | 0 | sPrefCached = true; |
263 | 0 | Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled"); |
264 | 0 | } |
265 | 0 |
|
266 | 0 | bool enabled = false; |
267 | 0 | if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED) { |
268 | 0 | enabled = true; |
269 | 0 | } else if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED) { |
270 | 0 | enabled = false; |
271 | 0 | } else { |
272 | 0 | if (sPrefCacheValue == 2) { |
273 | 0 | enabled = PlatformSupportsTouch(); |
274 | 0 |
|
275 | 0 | static bool firstTime = true; |
276 | 0 | // The touch screen data seems to be inaccurate in the parent process, |
277 | 0 | // and we really need the crash annotation in child processes. |
278 | 0 | if (firstTime && !XRE_IsParentProcess()) { |
279 | 0 | CrashReporter::AnnotateCrashReport( |
280 | 0 | CrashReporter::Annotation::HasDeviceTouchScreen, enabled); |
281 | 0 | firstTime = false; |
282 | 0 | } |
283 | 0 |
|
284 | 0 | #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) |
285 | 0 | if (enabled && aDocShell) { |
286 | 0 | // APZ might be disabled on this particular widget, in which case |
287 | 0 | // TouchEvent support will also be disabled. Try to detect that. |
288 | 0 | RefPtr<nsPresContext> pc; |
289 | 0 | aDocShell->GetPresContext(getter_AddRefs(pc)); |
290 | 0 | if (pc && pc->GetRootWidget()) { |
291 | 0 | enabled &= pc->GetRootWidget()->AsyncPanZoomEnabled(); |
292 | 0 | } |
293 | 0 | } |
294 | 0 | #endif |
295 | 0 | } else { |
296 | 0 | enabled = !!sPrefCacheValue; |
297 | 0 | } |
298 | 0 | } |
299 | 0 |
|
300 | 0 | if (enabled) { |
301 | 0 | nsContentUtils::InitializeTouchEventTable(); |
302 | 0 | } |
303 | 0 | return enabled; |
304 | 0 | } |
305 | | |
306 | | // static |
307 | | already_AddRefed<TouchEvent> |
308 | | TouchEvent::Constructor(const GlobalObject& aGlobal, |
309 | | const nsAString& aType, |
310 | | const TouchEventInit& aParam, |
311 | | ErrorResult& aRv) |
312 | 0 | { |
313 | 0 | nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); |
314 | 0 | RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr); |
315 | 0 | bool trusted = e->Init(t); |
316 | 0 | RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches); |
317 | 0 | RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches); |
318 | 0 | RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches); |
319 | 0 | e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, |
320 | 0 | aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey, |
321 | 0 | aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches, |
322 | 0 | changedTouches); |
323 | 0 | e->SetTrusted(trusted); |
324 | 0 | e->SetComposed(aParam.mComposed); |
325 | 0 | return e.forget(); |
326 | 0 | } |
327 | | |
328 | | |
329 | | already_AddRefed<TouchList> |
330 | | TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches) |
331 | 0 | { |
332 | 0 | RefPtr<TouchList> list = new TouchList(GetParentObject()); |
333 | 0 | size_t len = aTouches.Length(); |
334 | 0 | for (size_t i = 0; i < len; ++i) { |
335 | 0 | list->Append(aTouches[i]); |
336 | 0 | } |
337 | 0 | return list.forget(); |
338 | 0 | } |
339 | | |
340 | | bool |
341 | | TouchEvent::AltKey() |
342 | 0 | { |
343 | 0 | return mEvent->AsTouchEvent()->IsAlt(); |
344 | 0 | } |
345 | | |
346 | | bool |
347 | | TouchEvent::MetaKey() |
348 | 0 | { |
349 | 0 | return mEvent->AsTouchEvent()->IsMeta(); |
350 | 0 | } |
351 | | |
352 | | bool |
353 | | TouchEvent::CtrlKey() |
354 | 0 | { |
355 | 0 | return mEvent->AsTouchEvent()->IsControl(); |
356 | 0 | } |
357 | | |
358 | | bool |
359 | | TouchEvent::ShiftKey() |
360 | 0 | { |
361 | 0 | return mEvent->AsTouchEvent()->IsShift(); |
362 | 0 | } |
363 | | |
364 | | } // namespace dom |
365 | | } // namespace mozilla |
366 | | |
367 | | using namespace mozilla; |
368 | | using namespace mozilla::dom; |
369 | | |
370 | | already_AddRefed<TouchEvent> |
371 | | NS_NewDOMTouchEvent(EventTarget* aOwner, |
372 | | nsPresContext* aPresContext, |
373 | | WidgetTouchEvent* aEvent) |
374 | 0 | { |
375 | 0 | RefPtr<TouchEvent> it = new TouchEvent(aOwner, aPresContext, aEvent); |
376 | 0 | return it.forget(); |
377 | 0 | } |