Line data Source code
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 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TouchList, mParent, mPoints)
31 :
32 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(TouchList)
33 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(TouchList)
34 :
35 : JSObject*
36 0 : TouchList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
37 : {
38 0 : return TouchListBinding::Wrap(aCx, this, aGivenProto);
39 : }
40 :
41 : // static
42 : bool
43 0 : TouchList::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
44 : {
45 0 : return TouchEvent::PrefEnabled(aCx, aGlobal);
46 : }
47 :
48 : /******************************************************************************
49 : * TouchEvent
50 : *****************************************************************************/
51 :
52 0 : TouchEvent::TouchEvent(EventTarget* aOwner,
53 : nsPresContext* aPresContext,
54 0 : WidgetTouchEvent* aEvent)
55 : : UIEvent(aOwner, aPresContext,
56 : aEvent ? aEvent :
57 0 : new WidgetTouchEvent(false, eVoidEvent, nullptr))
58 : {
59 0 : if (aEvent) {
60 0 : mEventIsInternal = false;
61 :
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 : }
66 : } else {
67 0 : mEventIsInternal = true;
68 0 : mEvent->mTime = PR_Now();
69 : }
70 0 : }
71 :
72 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent, UIEvent,
73 : mTouches,
74 : mTargetTouches,
75 : mChangedTouches)
76 :
77 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TouchEvent)
78 0 : NS_INTERFACE_MAP_END_INHERITING(UIEvent)
79 :
80 0 : NS_IMPL_ADDREF_INHERITED(TouchEvent, UIEvent)
81 0 : NS_IMPL_RELEASE_INHERITED(TouchEvent, UIEvent)
82 :
83 : void
84 0 : TouchEvent::InitTouchEvent(const nsAString& aType,
85 : bool aCanBubble,
86 : bool aCancelable,
87 : nsGlobalWindow* aView,
88 : int32_t aDetail,
89 : bool aCtrlKey,
90 : bool aAltKey,
91 : bool aShiftKey,
92 : bool aMetaKey,
93 : TouchList* aTouches,
94 : TouchList* aTargetTouches,
95 : TouchList* aChangedTouches)
96 : {
97 0 : NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
98 :
99 0 : UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
100 0 : mEvent->AsInputEvent()->InitBasicModifiers(aCtrlKey, aAltKey,
101 0 : aShiftKey, aMetaKey);
102 0 : mTouches = aTouches;
103 0 : mTargetTouches = aTargetTouches;
104 0 : mChangedTouches = aChangedTouches;
105 : }
106 :
107 : TouchList*
108 0 : TouchEvent::Touches()
109 : {
110 0 : if (!mTouches) {
111 0 : WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
112 0 : if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) {
113 : // for touchend events, remove any changed touches from mTouches
114 0 : WidgetTouchEvent::AutoTouchArray unchangedTouches;
115 0 : const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
116 0 : for (uint32_t i = 0; i < touches.Length(); ++i) {
117 0 : if (!touches[i]->mChanged) {
118 0 : unchangedTouches.AppendElement(touches[i]);
119 : }
120 : }
121 0 : mTouches = new TouchList(ToSupports(this), unchangedTouches);
122 : } else {
123 0 : mTouches = new TouchList(ToSupports(this), touchEvent->mTouches);
124 : }
125 : }
126 0 : return mTouches;
127 : }
128 :
129 : TouchList*
130 0 : TouchEvent::TargetTouches()
131 : {
132 0 : if (!mTargetTouches) {
133 0 : WidgetTouchEvent::AutoTouchArray targetTouches;
134 0 : WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
135 0 : const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
136 0 : for (uint32_t i = 0; i < touches.Length(); ++i) {
137 : // for touchend/cancel events, don't append to the target list if this is a
138 : // touch that is ending
139 0 : if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) ||
140 0 : !touches[i]->mChanged) {
141 0 : if (touches[i]->mTarget == mEvent->mOriginalTarget) {
142 0 : targetTouches.AppendElement(touches[i]);
143 : }
144 : }
145 : }
146 0 : mTargetTouches = new TouchList(ToSupports(this), targetTouches);
147 : }
148 0 : return mTargetTouches;
149 : }
150 :
151 : TouchList*
152 0 : TouchEvent::ChangedTouches()
153 : {
154 0 : if (!mChangedTouches) {
155 0 : WidgetTouchEvent::AutoTouchArray changedTouches;
156 0 : WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
157 0 : const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
158 0 : for (uint32_t i = 0; i < touches.Length(); ++i) {
159 0 : if (touches[i]->mChanged) {
160 0 : changedTouches.AppendElement(touches[i]);
161 : }
162 : }
163 0 : mChangedTouches = new TouchList(ToSupports(this), changedTouches);
164 : }
165 0 : return mChangedTouches;
166 : }
167 :
168 : // static
169 : bool
170 24 : TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
171 : {
172 24 : nsIDocShell* docShell = nullptr;
173 24 : if (aGlobal) {
174 24 : nsGlobalWindow* win = xpc::WindowOrNull(aGlobal);
175 24 : if (win) {
176 24 : docShell = win->GetDocShell();
177 : }
178 : }
179 24 : return PrefEnabled(docShell);
180 : }
181 :
182 : // static
183 : bool
184 68 : TouchEvent::PrefEnabled(nsIDocShell* aDocShell)
185 : {
186 : static bool sPrefCached = false;
187 : static int32_t sPrefCacheValue = 0;
188 :
189 68 : uint32_t touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE;
190 68 : if (aDocShell) {
191 47 : aDocShell->GetTouchEventsOverride(&touchEventsOverride);
192 : }
193 :
194 68 : if (!sPrefCached) {
195 2 : sPrefCached = true;
196 2 : Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled");
197 : }
198 :
199 68 : bool enabled = false;
200 68 : if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED) {
201 0 : enabled = true;
202 68 : } else if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED) {
203 0 : enabled = false;
204 : } else {
205 68 : if (sPrefCacheValue == 2) {
206 : #if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
207 : // Touch support is always enabled on B2G and android.
208 : enabled = true;
209 : #elif defined(XP_WIN) || MOZ_WIDGET_GTK == 3
210 : static bool sDidCheckTouchDeviceSupport = false;
211 : static bool sIsTouchDeviceSupportPresent = false;
212 : // On Windows and GTK3 we auto-detect based on device support.
213 68 : if (!sDidCheckTouchDeviceSupport) {
214 2 : sDidCheckTouchDeviceSupport = true;
215 2 : sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
216 : // But touch events are only actually supported if APZ is enabled. If
217 : // APZ is disabled globally, we can check that once and incorporate that
218 : // into the cached state. If APZ is enabled, we need to further check
219 : // based on the widget, which we do below (and don't cache that result).
220 2 : sIsTouchDeviceSupportPresent &= gfxPlatform::AsyncPanZoomEnabled();
221 : }
222 68 : enabled = sIsTouchDeviceSupportPresent;
223 68 : if (enabled && aDocShell) {
224 : // APZ might be disabled on this particular widget, in which case
225 : // TouchEvent support will also be disabled. Try to detect that.
226 0 : RefPtr<nsPresContext> pc;
227 0 : aDocShell->GetPresContext(getter_AddRefs(pc));
228 0 : if (pc && pc->GetRootWidget()) {
229 0 : enabled &= pc->GetRootWidget()->AsyncPanZoomEnabled();
230 : }
231 : }
232 : #else
233 : enabled = false;
234 : #endif
235 : } else {
236 0 : enabled = !!sPrefCacheValue;
237 : }
238 : }
239 :
240 68 : if (enabled) {
241 0 : nsContentUtils::InitializeTouchEventTable();
242 : }
243 68 : return enabled;
244 : }
245 :
246 : // static
247 : already_AddRefed<Event>
248 0 : TouchEvent::Constructor(const GlobalObject& aGlobal,
249 : const nsAString& aType,
250 : const TouchEventInit& aParam,
251 : ErrorResult& aRv)
252 : {
253 0 : nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
254 0 : RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr);
255 0 : bool trusted = e->Init(t);
256 0 : RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
257 0 : RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
258 0 : RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
259 0 : e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
260 0 : aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
261 0 : aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
262 0 : changedTouches);
263 0 : e->SetTrusted(trusted);
264 0 : e->SetComposed(aParam.mComposed);
265 0 : return e.forget();
266 : }
267 :
268 :
269 : already_AddRefed<TouchList>
270 0 : TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches)
271 : {
272 0 : RefPtr<TouchList> list = new TouchList(GetParentObject());
273 0 : size_t len = aTouches.Length();
274 0 : for (size_t i = 0; i < len; ++i) {
275 0 : list->Append(aTouches[i]);
276 : }
277 0 : return list.forget();
278 : }
279 :
280 : bool
281 0 : TouchEvent::AltKey()
282 : {
283 0 : return mEvent->AsTouchEvent()->IsAlt();
284 : }
285 :
286 : bool
287 0 : TouchEvent::MetaKey()
288 : {
289 0 : return mEvent->AsTouchEvent()->IsMeta();
290 : }
291 :
292 : bool
293 0 : TouchEvent::CtrlKey()
294 : {
295 0 : return mEvent->AsTouchEvent()->IsControl();
296 : }
297 :
298 : bool
299 0 : TouchEvent::ShiftKey()
300 : {
301 0 : return mEvent->AsTouchEvent()->IsShift();
302 : }
303 :
304 : } // namespace dom
305 : } // namespace mozilla
306 :
307 : using namespace mozilla;
308 : using namespace mozilla::dom;
309 :
310 : already_AddRefed<TouchEvent>
311 0 : NS_NewDOMTouchEvent(EventTarget* aOwner,
312 : nsPresContext* aPresContext,
313 : WidgetTouchEvent* aEvent)
314 : {
315 0 : RefPtr<TouchEvent> it = new TouchEvent(aOwner, aPresContext, aEvent);
316 0 : return it.forget();
317 : }
|