Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/events/UIEvent.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 "base/basictypes.h"
8
#include "ipc/IPCMessageUtils.h"
9
#include "mozilla/dom/UIEvent.h"
10
#include "mozilla/ArrayUtils.h"
11
#include "mozilla/Assertions.h"
12
#include "mozilla/ContentEvents.h"
13
#include "mozilla/EventStateManager.h"
14
#include "mozilla/TextEvents.h"
15
#include "nsCOMPtr.h"
16
#include "nsContentUtils.h"
17
#include "nsIContent.h"
18
#include "nsIInterfaceRequestorUtils.h"
19
#include "nsIDocShell.h"
20
#include "nsIDOMWindow.h"
21
#include "nsIFrame.h"
22
#include "prtime.h"
23
24
namespace mozilla {
25
namespace dom {
26
27
UIEvent::UIEvent(EventTarget* aOwner,
28
                 nsPresContext* aPresContext,
29
                 WidgetGUIEvent* aEvent)
30
  : Event(aOwner, aPresContext,
31
          aEvent ? aEvent : new InternalUIEvent(false, eVoidEvent, nullptr))
32
  , mClientPoint(0, 0)
33
  , mLayerPoint(0, 0)
34
  , mPagePoint(0, 0)
35
  , mMovementPoint(0, 0)
36
  , mIsPointerLocked(EventStateManager::sIsPointerLocked)
37
  , mLastClientPoint(EventStateManager::sLastClientPoint)
38
0
{
39
0
  if (aEvent) {
40
0
    mEventIsInternal = false;
41
0
  }
42
0
  else {
43
0
    mEventIsInternal = true;
44
0
    mEvent->mTime = PR_Now();
45
0
  }
46
0
47
0
  // Fill mDetail and mView according to the mEvent (widget-generated
48
0
  // event) we've got
49
0
  switch(mEvent->mClass) {
50
0
    case eUIEventClass:
51
0
    {
52
0
      mDetail = mEvent->AsUIEvent()->mDetail;
53
0
      break;
54
0
    }
55
0
56
0
    case eScrollPortEventClass:
57
0
    {
58
0
      InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent();
59
0
      mDetail = static_cast<int32_t>(scrollEvent->mOrient);
60
0
      break;
61
0
    }
62
0
63
0
    default:
64
0
      mDetail = 0;
65
0
      break;
66
0
  }
67
0
68
0
  mView = nullptr;
69
0
  if (mPresContext)
70
0
  {
71
0
    nsIDocShell* docShell = mPresContext->GetDocShell();
72
0
    if (docShell)
73
0
    {
74
0
       mView = docShell->GetWindow();
75
0
    }
76
0
  }
77
0
}
78
79
// static
80
already_AddRefed<UIEvent>
81
UIEvent::Constructor(const GlobalObject& aGlobal,
82
                     const nsAString& aType,
83
                     const UIEventInit& aParam,
84
                     ErrorResult& aRv)
85
0
{
86
0
  nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
87
0
  RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr);
88
0
  bool trusted = e->Init(t);
89
0
  e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
90
0
                 aParam.mDetail);
91
0
  e->SetTrusted(trusted);
92
0
  e->SetComposed(aParam.mComposed);
93
0
  return e.forget();
94
0
}
95
96
NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event,
97
                                   mView)
98
99
NS_IMPL_ADDREF_INHERITED(UIEvent, Event)
100
NS_IMPL_RELEASE_INHERITED(UIEvent, Event)
101
102
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent)
103
0
NS_INTERFACE_MAP_END_INHERITING(Event)
104
105
static nsIntPoint
106
DevPixelsToCSSPixels(const LayoutDeviceIntPoint& aPoint,
107
                     nsPresContext* aContext)
108
0
{
109
0
  return nsIntPoint(aContext->DevPixelsToIntCSSPixels(aPoint.x),
110
0
                    aContext->DevPixelsToIntCSSPixels(aPoint.y));
111
0
}
112
113
nsIntPoint
114
UIEvent::GetMovementPoint()
115
0
{
116
0
  if (mEvent->mFlags.mIsPositionless) {
117
0
    return nsIntPoint(0, 0);
118
0
  }
119
0
120
0
  if (mPrivateDataDuplicated || mEventIsInternal) {
121
0
    return mMovementPoint;
122
0
  }
123
0
124
0
  if (!mEvent || !mEvent->AsGUIEvent()->mWidget ||
125
0
      (mEvent->mMessage != eMouseMove && mEvent->mMessage != ePointerMove)) {
126
0
    // Pointer Lock spec defines that movementX/Y must be zero for all mouse
127
0
    // events except mousemove.
128
0
    return nsIntPoint(0, 0);
129
0
  }
130
0
131
0
  // Calculate the delta between the last screen point and the current one.
132
0
  nsIntPoint current = DevPixelsToCSSPixels(mEvent->mRefPoint, mPresContext);
133
0
  nsIntPoint last = DevPixelsToCSSPixels(mEvent->mLastRefPoint, mPresContext);
134
0
  return current - last;
135
0
}
136
137
void
138
UIEvent::InitUIEvent(const nsAString& typeArg,
139
                     bool canBubbleArg,
140
                     bool cancelableArg,
141
                     nsGlobalWindowInner* viewArg,
142
                     int32_t detailArg)
143
0
{
144
0
  if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) {
145
0
    return;
146
0
  }
147
0
148
0
  Event::InitEvent(typeArg, canBubbleArg, cancelableArg);
149
0
150
0
  mDetail = detailArg;
151
0
  mView = viewArg ? viewArg->GetOuterWindow() : nullptr;
152
0
}
153
154
int32_t
155
UIEvent::PageX() const
156
0
{
157
0
  if (mEvent->mFlags.mIsPositionless) {
158
0
    return 0;
159
0
  }
160
0
161
0
  if (mPrivateDataDuplicated) {
162
0
    return mPagePoint.x;
163
0
  }
164
0
165
0
  return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint,
166
0
                              mClientPoint).x;
167
0
}
168
169
int32_t
170
UIEvent::PageY() const
171
0
{
172
0
  if (mEvent->mFlags.mIsPositionless) {
173
0
    return 0;
174
0
  }
175
0
176
0
  if (mPrivateDataDuplicated) {
177
0
    return mPagePoint.y;
178
0
  }
179
0
180
0
  return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint,
181
0
                              mClientPoint).y;
182
0
}
183
184
already_AddRefed<nsINode>
185
UIEvent::GetRangeParent()
186
0
{
187
0
  nsIFrame* targetFrame = nullptr;
188
0
189
0
  if (mPresContext) {
190
0
    nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
191
0
    if (shell) {
192
0
      shell->FlushPendingNotifications(FlushType::Layout);
193
0
      targetFrame = mPresContext->EventStateManager()->GetEventTarget();
194
0
    }
195
0
  }
196
0
197
0
  if (targetFrame) {
198
0
    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
199
0
                                                              targetFrame);
200
0
    nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
201
0
    if (parent) {
202
0
      if (parent->ChromeOnlyAccess() &&
203
0
          !nsContentUtils::CanAccessNativeAnon()) {
204
0
        return nullptr;
205
0
      }
206
0
      return parent.forget();
207
0
    }
208
0
  }
209
0
210
0
  return nullptr;
211
0
}
212
213
int32_t
214
UIEvent::RangeOffset() const
215
0
{
216
0
  if (!mPresContext) {
217
0
    return 0;
218
0
  }
219
0
220
0
  nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
221
0
  if (!shell) {
222
0
    return 0;
223
0
  }
224
0
225
0
  shell->FlushPendingNotifications(FlushType::Layout);
226
0
227
0
  nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
228
0
  if (!targetFrame) {
229
0
    return 0;
230
0
  }
231
0
232
0
  nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
233
0
                                                            targetFrame);
234
0
  return targetFrame->GetContentOffsetsFromPoint(pt).offset;
235
0
}
236
237
nsIntPoint
238
UIEvent::GetLayerPoint() const
239
0
{
240
0
  if (mEvent->mFlags.mIsPositionless) {
241
0
    return nsIntPoint(0, 0);
242
0
  }
243
0
244
0
  if (!mEvent ||
245
0
      (mEvent->mClass != eMouseEventClass &&
246
0
       mEvent->mClass != eMouseScrollEventClass &&
247
0
       mEvent->mClass != eWheelEventClass &&
248
0
       mEvent->mClass != ePointerEventClass &&
249
0
       mEvent->mClass != eTouchEventClass &&
250
0
       mEvent->mClass != eDragEventClass &&
251
0
       mEvent->mClass != eSimpleGestureEventClass) ||
252
0
      !mPresContext ||
253
0
      mEventIsInternal) {
254
0
    return mLayerPoint;
255
0
  }
256
0
  // XXX I'm not really sure this is correct; it's my best shot, though
257
0
  nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
258
0
  if (!targetFrame)
259
0
    return mLayerPoint;
260
0
  nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame);
261
0
  nsPoint pt(nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, layer));
262
0
  return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
263
0
                    nsPresContext::AppUnitsToIntCSSPixels(pt.y));
264
0
}
265
266
void
267
UIEvent::DuplicatePrivateData()
268
0
{
269
0
  mClientPoint =
270
0
    Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint,
271
0
                           mClientPoint);
272
0
  mMovementPoint = GetMovementPoint();
273
0
  mLayerPoint = GetLayerPoint();
274
0
  mPagePoint =
275
0
    Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint);
276
0
  // GetScreenPoint converts mEvent->mRefPoint to right coordinates.
277
0
  CSSIntPoint screenPoint =
278
0
    Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint);
279
0
280
0
  Event::DuplicatePrivateData();
281
0
282
0
  CSSToLayoutDeviceScale scale = mPresContext ? mPresContext->CSSToDevPixelScale()
283
0
                                              : CSSToLayoutDeviceScale(1);
284
0
  mEvent->mRefPoint = RoundedToInt(screenPoint * scale);
285
0
}
286
287
void
288
UIEvent::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType)
289
0
{
290
0
  if (aSerializeInterfaceType) {
291
0
    IPC::WriteParam(aMsg, NS_LITERAL_STRING("uievent"));
292
0
  }
293
0
294
0
  Event::Serialize(aMsg, false);
295
0
296
0
  IPC::WriteParam(aMsg, Detail());
297
0
}
298
299
bool
300
UIEvent::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter)
301
0
{
302
0
  NS_ENSURE_TRUE(Event::Deserialize(aMsg, aIter), false);
303
0
  NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &mDetail), false);
304
0
  return true;
305
0
}
306
307
// XXX Following struct and array are used only in
308
//     UIEvent::ComputeModifierState(), but if we define them in it,
309
//     we fail to build on Mac at calling mozilla::ArrayLength().
310
struct ModifierPair
311
{
312
  Modifier modifier;
313
  const char* name;
314
};
315
static const ModifierPair kPairs[] = {
316
  { MODIFIER_ALT,        NS_DOM_KEYNAME_ALT },
317
  { MODIFIER_ALTGRAPH,   NS_DOM_KEYNAME_ALTGRAPH },
318
  { MODIFIER_CAPSLOCK,   NS_DOM_KEYNAME_CAPSLOCK },
319
  { MODIFIER_CONTROL,    NS_DOM_KEYNAME_CONTROL },
320
  { MODIFIER_FN,         NS_DOM_KEYNAME_FN },
321
  { MODIFIER_FNLOCK,     NS_DOM_KEYNAME_FNLOCK },
322
  { MODIFIER_META,       NS_DOM_KEYNAME_META },
323
  { MODIFIER_NUMLOCK,    NS_DOM_KEYNAME_NUMLOCK },
324
  { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK },
325
  { MODIFIER_SHIFT,      NS_DOM_KEYNAME_SHIFT },
326
  { MODIFIER_SYMBOL,     NS_DOM_KEYNAME_SYMBOL },
327
  { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK },
328
  { MODIFIER_OS,         NS_DOM_KEYNAME_OS }
329
};
330
331
// static
332
Modifiers
333
UIEvent::ComputeModifierState(const nsAString& aModifiersList)
334
0
{
335
0
  if (aModifiersList.IsEmpty()) {
336
0
    return 0;
337
0
  }
338
0
339
0
  // Be careful about the performance.  If aModifiersList is too long,
340
0
  // parsing it needs too long time.
341
0
  // XXX Should we abort if aModifiersList is too long?
342
0
343
0
  Modifiers modifiers = 0;
344
0
345
0
  nsAString::const_iterator listStart, listEnd;
346
0
  aModifiersList.BeginReading(listStart);
347
0
  aModifiersList.EndReading(listEnd);
348
0
349
0
  for (uint32_t i = 0; i < ArrayLength(kPairs); i++) {
350
0
    nsAString::const_iterator start(listStart), end(listEnd);
351
0
    if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs[i].name), start, end)) {
352
0
      continue;
353
0
    }
354
0
355
0
    if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) ||
356
0
        (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) {
357
0
      continue;
358
0
    }
359
0
    modifiers |= kPairs[i].modifier;
360
0
  }
361
0
362
0
  return modifiers;
363
0
}
364
365
bool
366
UIEvent::GetModifierStateInternal(const nsAString& aKey)
367
0
{
368
0
  WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
369
0
  MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class");
370
0
  return ((inputEvent->mModifiers & WidgetInputEvent::GetModifier(aKey)) != 0);
371
0
}
372
373
void
374
UIEvent::InitModifiers(const EventModifierInit& aParam)
375
0
{
376
0
  if (NS_WARN_IF(!mEvent)) {
377
0
    return;
378
0
  }
379
0
  WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
380
0
  MOZ_ASSERT(inputEvent,
381
0
             "This method shouldn't be called if it doesn't have modifiers");
382
0
  if (NS_WARN_IF(!inputEvent)) {
383
0
    return;
384
0
  }
385
0
386
0
  inputEvent->mModifiers = MODIFIER_NONE;
387
0
388
0
#define SET_MODIFIER(aName, aValue) \
389
0
  if (aParam.m##aName) { \
390
0
    inputEvent->mModifiers |= aValue; \
391
0
  } \
392
0
393
0
  SET_MODIFIER(CtrlKey,                 MODIFIER_CONTROL)
394
0
  SET_MODIFIER(ShiftKey,                MODIFIER_SHIFT)
395
0
  SET_MODIFIER(AltKey,                  MODIFIER_ALT)
396
0
  SET_MODIFIER(MetaKey,                 MODIFIER_META)
397
0
  SET_MODIFIER(ModifierAltGraph,        MODIFIER_ALTGRAPH)
398
0
  SET_MODIFIER(ModifierCapsLock,        MODIFIER_CAPSLOCK)
399
0
  SET_MODIFIER(ModifierFn,              MODIFIER_FN)
400
0
  SET_MODIFIER(ModifierFnLock,          MODIFIER_FNLOCK)
401
0
  SET_MODIFIER(ModifierNumLock,         MODIFIER_NUMLOCK)
402
0
  SET_MODIFIER(ModifierOS,              MODIFIER_OS)
403
0
  SET_MODIFIER(ModifierScrollLock,      MODIFIER_SCROLLLOCK)
404
0
  SET_MODIFIER(ModifierSymbol,          MODIFIER_SYMBOL)
405
0
  SET_MODIFIER(ModifierSymbolLock,      MODIFIER_SYMBOLLOCK)
406
0
407
0
#undef SET_MODIFIER
408
0
}
409
410
} // namespace dom
411
} // namespace mozilla
412
413
using namespace mozilla;
414
using namespace mozilla::dom;
415
416
already_AddRefed<UIEvent>
417
NS_NewDOMUIEvent(EventTarget* aOwner,
418
                 nsPresContext* aPresContext,
419
                 WidgetGUIEvent* aEvent)
420
0
{
421
0
  RefPtr<UIEvent> it = new UIEvent(aOwner, aPresContext, aEvent);
422
0
  return it.forget();
423
0
}