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
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList)25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList)
26 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
27 NS_INTERFACE_MAP_ENTRY(nsISupports)
28 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 {
38 return TouchListBinding::Wrap(aCx, this, aGivenProto);
39 }
40
41 // static
42 bool
PrefEnabled(JSContext * aCx,JSObject * aGlobal)43 TouchList::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
44 {
45 return TouchEvent::PrefEnabled(aCx, aGlobal);
46 }
47
48 /******************************************************************************
49 * TouchEvent
50 *****************************************************************************/
51
TouchEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetTouchEvent * aEvent)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 {
59 if (aEvent) {
60 mEventIsInternal = false;
61
62 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
63 Touch* touch = aEvent->mTouches[i];
64 touch->InitializePoints(mPresContext, aEvent);
65 }
66 } else {
67 mEventIsInternal = true;
68 mEvent->mTime = PR_Now();
69 }
70 }
71
NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent,UIEvent,mTouches,mTargetTouches,mChangedTouches)72 NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent, UIEvent,
73 mTouches,
74 mTargetTouches,
75 mChangedTouches)
76
77 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TouchEvent)
78 NS_INTERFACE_MAP_END_INHERITING(UIEvent)
79
80 NS_IMPL_ADDREF_INHERITED(TouchEvent, UIEvent)
81 NS_IMPL_RELEASE_INHERITED(TouchEvent, UIEvent)
82
83 void
84 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 NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
98
99 UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
100 mEvent->AsInputEvent()->InitBasicModifiers(aCtrlKey, aAltKey,
101 aShiftKey, aMetaKey);
102 mTouches = aTouches;
103 mTargetTouches = aTargetTouches;
104 mChangedTouches = aChangedTouches;
105 }
106
107 TouchList*
Touches()108 TouchEvent::Touches()
109 {
110 if (!mTouches) {
111 WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
112 if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) {
113 // for touchend events, remove any changed touches from mTouches
114 WidgetTouchEvent::AutoTouchArray unchangedTouches;
115 const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
116 for (uint32_t i = 0; i < touches.Length(); ++i) {
117 if (!touches[i]->mChanged) {
118 unchangedTouches.AppendElement(touches[i]);
119 }
120 }
121 mTouches = new TouchList(ToSupports(this), unchangedTouches);
122 } else {
123 mTouches = new TouchList(ToSupports(this), touchEvent->mTouches);
124 }
125 }
126 return mTouches;
127 }
128
129 TouchList*
TargetTouches()130 TouchEvent::TargetTouches()
131 {
132 if (!mTargetTouches) {
133 WidgetTouchEvent::AutoTouchArray targetTouches;
134 WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
135 const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
136 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 if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) ||
140 !touches[i]->mChanged) {
141 if (touches[i]->mTarget == mEvent->mOriginalTarget) {
142 targetTouches.AppendElement(touches[i]);
143 }
144 }
145 }
146 mTargetTouches = new TouchList(ToSupports(this), targetTouches);
147 }
148 return mTargetTouches;
149 }
150
151 TouchList*
ChangedTouches()152 TouchEvent::ChangedTouches()
153 {
154 if (!mChangedTouches) {
155 WidgetTouchEvent::AutoTouchArray changedTouches;
156 WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
157 const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
158 for (uint32_t i = 0; i < touches.Length(); ++i) {
159 if (touches[i]->mChanged) {
160 changedTouches.AppendElement(touches[i]);
161 }
162 }
163 mChangedTouches = new TouchList(ToSupports(this), changedTouches);
164 }
165 return mChangedTouches;
166 }
167
168 // static
169 bool
PrefEnabled(JSContext * aCx,JSObject * aGlobal)170 TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
171 {
172 nsIDocShell* docShell = nullptr;
173 if (aGlobal) {
174 nsGlobalWindow* win = xpc::WindowOrNull(aGlobal);
175 if (win) {
176 docShell = win->GetDocShell();
177 }
178 }
179 return PrefEnabled(docShell);
180 }
181
182 // static
183 bool
PrefEnabled(nsIDocShell * aDocShell)184 TouchEvent::PrefEnabled(nsIDocShell* aDocShell)
185 {
186 static bool sPrefCached = false;
187 static int32_t sPrefCacheValue = 0;
188
189 uint32_t touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE;
190 if (aDocShell) {
191 aDocShell->GetTouchEventsOverride(&touchEventsOverride);
192 }
193
194 if (!sPrefCached) {
195 sPrefCached = true;
196 Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled");
197 }
198
199 bool enabled = false;
200 if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED) {
201 enabled = true;
202 } else if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED) {
203 enabled = false;
204 } else {
205 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 if (!sDidCheckTouchDeviceSupport) {
214 sDidCheckTouchDeviceSupport = true;
215 sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
216 }
217 enabled = sIsTouchDeviceSupportPresent;
218 #else
219 enabled = false;
220 #endif
221 } else {
222 enabled = !!sPrefCacheValue;
223 }
224 }
225
226 if (enabled) {
227 nsContentUtils::InitializeTouchEventTable();
228 }
229 return enabled;
230 }
231
232 // static
233 already_AddRefed<Event>
Constructor(const GlobalObject & aGlobal,const nsAString & aType,const TouchEventInit & aParam,ErrorResult & aRv)234 TouchEvent::Constructor(const GlobalObject& aGlobal,
235 const nsAString& aType,
236 const TouchEventInit& aParam,
237 ErrorResult& aRv)
238 {
239 nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
240 RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr);
241 bool trusted = e->Init(t);
242 RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
243 RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
244 RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
245 e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
246 aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
247 aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
248 changedTouches);
249 e->SetTrusted(trusted);
250 e->SetComposed(aParam.mComposed);
251 return e.forget();
252 }
253
254
255 already_AddRefed<TouchList>
CopyTouches(const Sequence<OwningNonNull<Touch>> & aTouches)256 TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches)
257 {
258 RefPtr<TouchList> list = new TouchList(GetParentObject());
259 size_t len = aTouches.Length();
260 for (size_t i = 0; i < len; ++i) {
261 list->Append(aTouches[i]);
262 }
263 return list.forget();
264 }
265
266 bool
AltKey()267 TouchEvent::AltKey()
268 {
269 return mEvent->AsTouchEvent()->IsAlt();
270 }
271
272 bool
MetaKey()273 TouchEvent::MetaKey()
274 {
275 return mEvent->AsTouchEvent()->IsMeta();
276 }
277
278 bool
CtrlKey()279 TouchEvent::CtrlKey()
280 {
281 return mEvent->AsTouchEvent()->IsControl();
282 }
283
284 bool
ShiftKey()285 TouchEvent::ShiftKey()
286 {
287 return mEvent->AsTouchEvent()->IsShift();
288 }
289
290 } // namespace dom
291 } // namespace mozilla
292
293 using namespace mozilla;
294 using namespace mozilla::dom;
295
296 already_AddRefed<TouchEvent>
NS_NewDOMTouchEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetTouchEvent * aEvent)297 NS_NewDOMTouchEvent(EventTarget* aOwner,
298 nsPresContext* aPresContext,
299 WidgetTouchEvent* aEvent)
300 {
301 RefPtr<TouchEvent> it = new TouchEvent(aOwner, aPresContext, aEvent);
302 return it.forget();
303 }
304