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