Coverage Report

Created: 2018-09-25 14:53

/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
}