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