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