|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "base/basictypes.h" |
|
7 #include "ipc/IPCMessageUtils.h" |
|
8 #include "mozilla/dom/UIEvent.h" |
|
9 #include "mozilla/ArrayUtils.h" |
|
10 #include "mozilla/Assertions.h" |
|
11 #include "mozilla/ContentEvents.h" |
|
12 #include "mozilla/EventStateManager.h" |
|
13 #include "mozilla/TextEvents.h" |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsContentUtils.h" |
|
16 #include "nsIContent.h" |
|
17 #include "nsIInterfaceRequestorUtils.h" |
|
18 #include "nsIDOMWindow.h" |
|
19 #include "nsIDOMNode.h" |
|
20 #include "nsIFrame.h" |
|
21 #include "prtime.h" |
|
22 |
|
23 namespace mozilla { |
|
24 namespace dom { |
|
25 |
|
26 UIEvent::UIEvent(EventTarget* aOwner, |
|
27 nsPresContext* aPresContext, |
|
28 WidgetGUIEvent* aEvent) |
|
29 : Event(aOwner, aPresContext, |
|
30 aEvent ? aEvent : new InternalUIEvent(false, 0)) |
|
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 { |
|
38 if (aEvent) { |
|
39 mEventIsInternal = false; |
|
40 } |
|
41 else { |
|
42 mEventIsInternal = true; |
|
43 mEvent->time = PR_Now(); |
|
44 } |
|
45 |
|
46 // Fill mDetail and mView according to the mEvent (widget-generated |
|
47 // event) we've got |
|
48 switch(mEvent->eventStructType) |
|
49 { |
|
50 case NS_UI_EVENT: |
|
51 { |
|
52 mDetail = mEvent->AsUIEvent()->detail; |
|
53 break; |
|
54 } |
|
55 |
|
56 case NS_SCROLLPORT_EVENT: |
|
57 { |
|
58 InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent(); |
|
59 mDetail = (int32_t)scrollEvent->orient; |
|
60 break; |
|
61 } |
|
62 |
|
63 default: |
|
64 mDetail = 0; |
|
65 break; |
|
66 } |
|
67 |
|
68 mView = nullptr; |
|
69 if (mPresContext) |
|
70 { |
|
71 nsISupports* container = mPresContext->GetContainerWeak(); |
|
72 if (container) |
|
73 { |
|
74 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container); |
|
75 if (window) |
|
76 mView = do_QueryInterface(window); |
|
77 } |
|
78 } |
|
79 } |
|
80 |
|
81 // static |
|
82 already_AddRefed<UIEvent> |
|
83 UIEvent::Constructor(const GlobalObject& aGlobal, |
|
84 const nsAString& aType, |
|
85 const UIEventInit& aParam, |
|
86 ErrorResult& aRv) |
|
87 { |
|
88 nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); |
|
89 nsRefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr); |
|
90 bool trusted = e->Init(t); |
|
91 aRv = e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, |
|
92 aParam.mDetail); |
|
93 e->SetTrusted(trusted); |
|
94 return e.forget(); |
|
95 } |
|
96 |
|
97 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, |
|
98 mView) |
|
99 |
|
100 NS_IMPL_ADDREF_INHERITED(UIEvent, Event) |
|
101 NS_IMPL_RELEASE_INHERITED(UIEvent, Event) |
|
102 |
|
103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UIEvent) |
|
104 NS_INTERFACE_MAP_ENTRY(nsIDOMUIEvent) |
|
105 NS_INTERFACE_MAP_END_INHERITING(Event) |
|
106 |
|
107 static nsIntPoint |
|
108 DevPixelsToCSSPixels(const LayoutDeviceIntPoint& aPoint, |
|
109 nsPresContext* aContext) |
|
110 { |
|
111 return nsIntPoint(aContext->DevPixelsToIntCSSPixels(aPoint.x), |
|
112 aContext->DevPixelsToIntCSSPixels(aPoint.y)); |
|
113 } |
|
114 |
|
115 nsIntPoint |
|
116 UIEvent::GetMovementPoint() |
|
117 { |
|
118 if (mPrivateDataDuplicated) { |
|
119 return mMovementPoint; |
|
120 } |
|
121 |
|
122 if (!mEvent || |
|
123 (mEvent->eventStructType != NS_MOUSE_EVENT && |
|
124 mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && |
|
125 mEvent->eventStructType != NS_WHEEL_EVENT && |
|
126 mEvent->eventStructType != NS_DRAG_EVENT && |
|
127 mEvent->eventStructType != NS_POINTER_EVENT && |
|
128 mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || |
|
129 !mEvent->AsGUIEvent()->widget) { |
|
130 return nsIntPoint(0, 0); |
|
131 } |
|
132 |
|
133 // Calculate the delta between the last screen point and the current one. |
|
134 nsIntPoint current = DevPixelsToCSSPixels(mEvent->refPoint, mPresContext); |
|
135 nsIntPoint last = DevPixelsToCSSPixels(mEvent->lastRefPoint, mPresContext); |
|
136 return current - last; |
|
137 } |
|
138 |
|
139 NS_IMETHODIMP |
|
140 UIEvent::GetView(nsIDOMWindow** aView) |
|
141 { |
|
142 *aView = mView; |
|
143 NS_IF_ADDREF(*aView); |
|
144 return NS_OK; |
|
145 } |
|
146 |
|
147 NS_IMETHODIMP |
|
148 UIEvent::GetDetail(int32_t* aDetail) |
|
149 { |
|
150 *aDetail = mDetail; |
|
151 return NS_OK; |
|
152 } |
|
153 |
|
154 NS_IMETHODIMP |
|
155 UIEvent::InitUIEvent(const nsAString& typeArg, |
|
156 bool canBubbleArg, |
|
157 bool cancelableArg, |
|
158 nsIDOMWindow* viewArg, |
|
159 int32_t detailArg) |
|
160 { |
|
161 if (viewArg) { |
|
162 nsCOMPtr<nsPIDOMWindow> view = do_QueryInterface(viewArg); |
|
163 NS_ENSURE_TRUE(view, NS_ERROR_INVALID_ARG); |
|
164 } |
|
165 nsresult rv = Event::InitEvent(typeArg, canBubbleArg, cancelableArg); |
|
166 NS_ENSURE_SUCCESS(rv, rv); |
|
167 |
|
168 mDetail = detailArg; |
|
169 mView = viewArg; |
|
170 |
|
171 return NS_OK; |
|
172 } |
|
173 |
|
174 NS_IMETHODIMP |
|
175 UIEvent::GetPageX(int32_t* aPageX) |
|
176 { |
|
177 NS_ENSURE_ARG_POINTER(aPageX); |
|
178 *aPageX = PageX(); |
|
179 return NS_OK; |
|
180 } |
|
181 |
|
182 int32_t |
|
183 UIEvent::PageX() const |
|
184 { |
|
185 if (mPrivateDataDuplicated) { |
|
186 return mPagePoint.x; |
|
187 } |
|
188 |
|
189 return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, |
|
190 mClientPoint).x; |
|
191 } |
|
192 |
|
193 NS_IMETHODIMP |
|
194 UIEvent::GetPageY(int32_t* aPageY) |
|
195 { |
|
196 NS_ENSURE_ARG_POINTER(aPageY); |
|
197 *aPageY = PageY(); |
|
198 return NS_OK; |
|
199 } |
|
200 |
|
201 int32_t |
|
202 UIEvent::PageY() const |
|
203 { |
|
204 if (mPrivateDataDuplicated) { |
|
205 return mPagePoint.y; |
|
206 } |
|
207 |
|
208 return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, |
|
209 mClientPoint).y; |
|
210 } |
|
211 |
|
212 NS_IMETHODIMP |
|
213 UIEvent::GetWhich(uint32_t* aWhich) |
|
214 { |
|
215 NS_ENSURE_ARG_POINTER(aWhich); |
|
216 *aWhich = Which(); |
|
217 return NS_OK; |
|
218 } |
|
219 |
|
220 already_AddRefed<nsINode> |
|
221 UIEvent::GetRangeParent() |
|
222 { |
|
223 nsIFrame* targetFrame = nullptr; |
|
224 |
|
225 if (mPresContext) { |
|
226 targetFrame = mPresContext->EventStateManager()->GetEventTarget(); |
|
227 } |
|
228 |
|
229 if (targetFrame) { |
|
230 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, |
|
231 targetFrame); |
|
232 nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content; |
|
233 if (parent) { |
|
234 if (parent->ChromeOnlyAccess() && |
|
235 !nsContentUtils::CanAccessNativeAnon()) { |
|
236 return nullptr; |
|
237 } |
|
238 return parent.forget(); |
|
239 } |
|
240 } |
|
241 |
|
242 return nullptr; |
|
243 } |
|
244 |
|
245 NS_IMETHODIMP |
|
246 UIEvent::GetRangeParent(nsIDOMNode** aRangeParent) |
|
247 { |
|
248 NS_ENSURE_ARG_POINTER(aRangeParent); |
|
249 *aRangeParent = nullptr; |
|
250 nsCOMPtr<nsINode> n = GetRangeParent(); |
|
251 if (n) { |
|
252 CallQueryInterface(n, aRangeParent); |
|
253 } |
|
254 return NS_OK; |
|
255 } |
|
256 |
|
257 NS_IMETHODIMP |
|
258 UIEvent::GetRangeOffset(int32_t* aRangeOffset) |
|
259 { |
|
260 NS_ENSURE_ARG_POINTER(aRangeOffset); |
|
261 *aRangeOffset = RangeOffset(); |
|
262 return NS_OK; |
|
263 } |
|
264 |
|
265 int32_t |
|
266 UIEvent::RangeOffset() const |
|
267 { |
|
268 if (!mPresContext) { |
|
269 return 0; |
|
270 } |
|
271 |
|
272 nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); |
|
273 if (!targetFrame) { |
|
274 return 0; |
|
275 } |
|
276 |
|
277 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, |
|
278 targetFrame); |
|
279 return targetFrame->GetContentOffsetsFromPoint(pt).offset; |
|
280 } |
|
281 |
|
282 NS_IMETHODIMP |
|
283 UIEvent::GetCancelBubble(bool* aCancelBubble) |
|
284 { |
|
285 NS_ENSURE_ARG_POINTER(aCancelBubble); |
|
286 *aCancelBubble = CancelBubble(); |
|
287 return NS_OK; |
|
288 } |
|
289 |
|
290 NS_IMETHODIMP |
|
291 UIEvent::SetCancelBubble(bool aCancelBubble) |
|
292 { |
|
293 mEvent->mFlags.mPropagationStopped = aCancelBubble; |
|
294 return NS_OK; |
|
295 } |
|
296 |
|
297 nsIntPoint |
|
298 UIEvent::GetLayerPoint() const |
|
299 { |
|
300 if (!mEvent || |
|
301 (mEvent->eventStructType != NS_MOUSE_EVENT && |
|
302 mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && |
|
303 mEvent->eventStructType != NS_WHEEL_EVENT && |
|
304 mEvent->eventStructType != NS_POINTER_EVENT && |
|
305 mEvent->eventStructType != NS_TOUCH_EVENT && |
|
306 mEvent->eventStructType != NS_DRAG_EVENT && |
|
307 mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || |
|
308 !mPresContext || |
|
309 mEventIsInternal) { |
|
310 return mLayerPoint; |
|
311 } |
|
312 // XXX I'm not really sure this is correct; it's my best shot, though |
|
313 nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); |
|
314 if (!targetFrame) |
|
315 return mLayerPoint; |
|
316 nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame); |
|
317 nsPoint pt(nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, layer)); |
|
318 return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), |
|
319 nsPresContext::AppUnitsToIntCSSPixels(pt.y)); |
|
320 } |
|
321 |
|
322 NS_IMETHODIMP |
|
323 UIEvent::GetLayerX(int32_t* aLayerX) |
|
324 { |
|
325 NS_ENSURE_ARG_POINTER(aLayerX); |
|
326 *aLayerX = GetLayerPoint().x; |
|
327 return NS_OK; |
|
328 } |
|
329 |
|
330 NS_IMETHODIMP |
|
331 UIEvent::GetLayerY(int32_t* aLayerY) |
|
332 { |
|
333 NS_ENSURE_ARG_POINTER(aLayerY); |
|
334 *aLayerY = GetLayerPoint().y; |
|
335 return NS_OK; |
|
336 } |
|
337 |
|
338 NS_IMETHODIMP |
|
339 UIEvent::GetIsChar(bool* aIsChar) |
|
340 { |
|
341 *aIsChar = IsChar(); |
|
342 return NS_OK; |
|
343 } |
|
344 |
|
345 bool |
|
346 UIEvent::IsChar() const |
|
347 { |
|
348 WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent(); |
|
349 if (keyEvent) { |
|
350 return keyEvent->isChar; |
|
351 } |
|
352 WidgetTextEvent* textEvent = mEvent->AsTextEvent(); |
|
353 return textEvent ? textEvent->isChar : false; |
|
354 } |
|
355 |
|
356 NS_IMETHODIMP |
|
357 UIEvent::DuplicatePrivateData() |
|
358 { |
|
359 mClientPoint = |
|
360 Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint, |
|
361 mClientPoint); |
|
362 mMovementPoint = GetMovementPoint(); |
|
363 mLayerPoint = GetLayerPoint(); |
|
364 mPagePoint = |
|
365 Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, mClientPoint); |
|
366 // GetScreenPoint converts mEvent->refPoint to right coordinates. |
|
367 nsIntPoint screenPoint = |
|
368 Event::GetScreenCoords(mPresContext, mEvent, mEvent->refPoint); |
|
369 nsresult rv = Event::DuplicatePrivateData(); |
|
370 if (NS_SUCCEEDED(rv)) { |
|
371 mEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(screenPoint); |
|
372 } |
|
373 return rv; |
|
374 } |
|
375 |
|
376 NS_IMETHODIMP_(void) |
|
377 UIEvent::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) |
|
378 { |
|
379 if (aSerializeInterfaceType) { |
|
380 IPC::WriteParam(aMsg, NS_LITERAL_STRING("uievent")); |
|
381 } |
|
382 |
|
383 Event::Serialize(aMsg, false); |
|
384 |
|
385 int32_t detail = 0; |
|
386 GetDetail(&detail); |
|
387 IPC::WriteParam(aMsg, detail); |
|
388 } |
|
389 |
|
390 NS_IMETHODIMP_(bool) |
|
391 UIEvent::Deserialize(const IPC::Message* aMsg, void** aIter) |
|
392 { |
|
393 NS_ENSURE_TRUE(Event::Deserialize(aMsg, aIter), false); |
|
394 NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &mDetail), false); |
|
395 return true; |
|
396 } |
|
397 |
|
398 // XXX Following struct and array are used only in |
|
399 // UIEvent::ComputeModifierState(), but if we define them in it, |
|
400 // we fail to build on Mac at calling mozilla::ArrayLength(). |
|
401 struct ModifierPair |
|
402 { |
|
403 Modifier modifier; |
|
404 const char* name; |
|
405 }; |
|
406 static const ModifierPair kPairs[] = { |
|
407 { MODIFIER_ALT, NS_DOM_KEYNAME_ALT }, |
|
408 { MODIFIER_ALTGRAPH, NS_DOM_KEYNAME_ALTGRAPH }, |
|
409 { MODIFIER_CAPSLOCK, NS_DOM_KEYNAME_CAPSLOCK }, |
|
410 { MODIFIER_CONTROL, NS_DOM_KEYNAME_CONTROL }, |
|
411 { MODIFIER_FN, NS_DOM_KEYNAME_FN }, |
|
412 { MODIFIER_META, NS_DOM_KEYNAME_META }, |
|
413 { MODIFIER_NUMLOCK, NS_DOM_KEYNAME_NUMLOCK }, |
|
414 { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK }, |
|
415 { MODIFIER_SHIFT, NS_DOM_KEYNAME_SHIFT }, |
|
416 { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK }, |
|
417 { MODIFIER_OS, NS_DOM_KEYNAME_OS } |
|
418 }; |
|
419 |
|
420 // static |
|
421 Modifiers |
|
422 UIEvent::ComputeModifierState(const nsAString& aModifiersList) |
|
423 { |
|
424 if (aModifiersList.IsEmpty()) { |
|
425 return 0; |
|
426 } |
|
427 |
|
428 // Be careful about the performance. If aModifiersList is too long, |
|
429 // parsing it needs too long time. |
|
430 // XXX Should we abort if aModifiersList is too long? |
|
431 |
|
432 Modifiers modifiers = 0; |
|
433 |
|
434 nsAString::const_iterator listStart, listEnd; |
|
435 aModifiersList.BeginReading(listStart); |
|
436 aModifiersList.EndReading(listEnd); |
|
437 |
|
438 for (uint32_t i = 0; i < ArrayLength(kPairs); i++) { |
|
439 nsAString::const_iterator start(listStart), end(listEnd); |
|
440 if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs[i].name), start, end)) { |
|
441 continue; |
|
442 } |
|
443 |
|
444 if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) || |
|
445 (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) { |
|
446 continue; |
|
447 } |
|
448 modifiers |= kPairs[i].modifier; |
|
449 } |
|
450 |
|
451 return modifiers; |
|
452 } |
|
453 |
|
454 bool |
|
455 UIEvent::GetModifierStateInternal(const nsAString& aKey) |
|
456 { |
|
457 WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); |
|
458 MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class"); |
|
459 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SHIFT)) { |
|
460 return inputEvent->IsShift(); |
|
461 } |
|
462 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CONTROL)) { |
|
463 return inputEvent->IsControl(); |
|
464 } |
|
465 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_META)) { |
|
466 return inputEvent->IsMeta(); |
|
467 } |
|
468 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALT)) { |
|
469 return inputEvent->IsAlt(); |
|
470 } |
|
471 |
|
472 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALTGRAPH)) { |
|
473 return inputEvent->IsAltGraph(); |
|
474 } |
|
475 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_OS)) { |
|
476 return inputEvent->IsOS(); |
|
477 } |
|
478 |
|
479 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CAPSLOCK)) { |
|
480 return inputEvent->IsCapsLocked(); |
|
481 } |
|
482 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_NUMLOCK)) { |
|
483 return inputEvent->IsNumLocked(); |
|
484 } |
|
485 |
|
486 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_FN)) { |
|
487 return inputEvent->IsFn(); |
|
488 } |
|
489 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SCROLLLOCK)) { |
|
490 return inputEvent->IsScrollLocked(); |
|
491 } |
|
492 if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SYMBOLLOCK)) { |
|
493 return inputEvent->IsSymbolLocked(); |
|
494 } |
|
495 return false; |
|
496 } |
|
497 |
|
498 } // namespace dom |
|
499 } // namespace mozilla |
|
500 |
|
501 using namespace mozilla; |
|
502 using namespace mozilla::dom; |
|
503 |
|
504 nsresult |
|
505 NS_NewDOMUIEvent(nsIDOMEvent** aInstancePtrResult, |
|
506 EventTarget* aOwner, |
|
507 nsPresContext* aPresContext, |
|
508 WidgetGUIEvent* aEvent) |
|
509 { |
|
510 UIEvent* it = new UIEvent(aOwner, aPresContext, aEvent); |
|
511 NS_ADDREF(it); |
|
512 *aInstancePtrResult = static_cast<Event*>(it); |
|
513 return NS_OK; |
|
514 } |