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