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/. */
7 #include "base/basictypes.h"
8 #include "ipc/IPCMessageUtils.h"
9 #include "ipc/IPCMessageUtilsSpecializations.h"
10 #include "mozilla/dom/UIEvent.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/ContentEvents.h"
14 #include "mozilla/EventStateManager.h"
15 #include "mozilla/PointerLockManager.h"
16 #include "mozilla/PresShell.h"
17 #include "mozilla/TextEvents.h"
19 #include "nsContentUtils.h"
20 #include "nsIContent.h"
21 #include "nsIInterfaceRequestorUtils.h"
22 #include "nsIDocShell.h"
24 #include "nsLayoutUtils.h"
27 namespace mozilla::dom
{
29 UIEvent::UIEvent(EventTarget
* aOwner
, nsPresContext
* aPresContext
,
30 WidgetGUIEvent
* aEvent
)
31 : Event(aOwner
, aPresContext
,
32 aEvent
? aEvent
: new InternalUIEvent(false, eVoidEvent
, nullptr)),
33 mDefaultClientPoint(0, 0),
37 mIsPointerLocked(PointerLockManager::IsLocked()),
38 mLastClientPoint(EventStateManager::sLastClientPoint
) {
40 mEventIsInternal
= false;
42 mEventIsInternal
= true;
45 // Fill mDetail and mView according to the mEvent (widget-generated
47 switch (mEvent
->mClass
) {
49 mDetail
= mEvent
->AsUIEvent()->mDetail
;
53 case eScrollPortEventClass
: {
54 InternalScrollPortEvent
* scrollEvent
= mEvent
->AsScrollPortEvent();
55 mDetail
= static_cast<int32_t>(scrollEvent
->mOrient
);
66 nsIDocShell
* docShell
= mPresContext
->GetDocShell();
68 mView
= docShell
->GetWindow();
74 already_AddRefed
<UIEvent
> UIEvent::Constructor(const GlobalObject
& aGlobal
,
75 const nsAString
& aType
,
76 const UIEventInit
& aParam
) {
77 nsCOMPtr
<EventTarget
> t
= do_QueryInterface(aGlobal
.GetAsSupports());
78 RefPtr
<UIEvent
> e
= new UIEvent(t
, nullptr, nullptr);
79 bool trusted
= e
->Init(t
);
80 e
->InitUIEvent(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
82 e
->SetTrusted(trusted
);
83 e
->SetComposed(aParam
.mComposed
);
87 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent
, Event
, mView
)
89 NS_IMPL_ADDREF_INHERITED(UIEvent
, Event
)
90 NS_IMPL_RELEASE_INHERITED(UIEvent
, Event
)
92 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent
)
93 NS_INTERFACE_MAP_END_INHERITING(Event
)
95 static nsIntPoint
DevPixelsToCSSPixels(const LayoutDeviceIntPoint
& aPoint
,
96 nsPresContext
* aContext
) {
97 return nsIntPoint(aContext
->DevPixelsToIntCSSPixels(aPoint
.x
),
98 aContext
->DevPixelsToIntCSSPixels(aPoint
.y
));
101 nsIntPoint
UIEvent::GetMovementPoint() {
102 if (mEvent
->mFlags
.mIsPositionless
) {
103 return nsIntPoint(0, 0);
106 if (mPrivateDataDuplicated
|| mEventIsInternal
) {
107 return mMovementPoint
;
110 if (!mEvent
|| !mEvent
->AsGUIEvent()->mWidget
||
111 (mEvent
->mMessage
!= eMouseMove
&& mEvent
->mMessage
!= ePointerMove
)) {
112 // Pointer Lock spec defines that movementX/Y must be zero for all mouse
113 // events except mousemove.
114 return nsIntPoint(0, 0);
117 // Calculate the delta between the last screen point and the current one.
118 nsIntPoint current
= DevPixelsToCSSPixels(mEvent
->mRefPoint
, mPresContext
);
119 nsIntPoint last
= DevPixelsToCSSPixels(mEvent
->mLastRefPoint
, mPresContext
);
120 return current
- last
;
123 void UIEvent::InitUIEvent(const nsAString
& typeArg
, bool canBubbleArg
,
124 bool cancelableArg
, nsGlobalWindowInner
* viewArg
,
126 if (NS_WARN_IF(mEvent
->mFlags
.mIsBeingDispatched
)) {
130 Event::InitEvent(typeArg
, canBubbleArg
, cancelableArg
);
133 mView
= viewArg
? viewArg
->GetOuterWindow() : nullptr;
136 already_AddRefed
<nsIContent
> UIEvent::GetRangeParentContentAndOffset(
137 int32_t* aOffset
) const {
138 if (NS_WARN_IF(!mPresContext
)) {
141 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
142 if (NS_WARN_IF(!presShell
)) {
145 nsCOMPtr
<nsIContent
> container
;
146 nsLayoutUtils::GetContainerAndOffsetAtEvent(
147 presShell
, mEvent
, getter_AddRefs(container
), aOffset
);
148 return container
.forget();
151 int32_t UIEvent::RangeOffset() const {
152 if (NS_WARN_IF(!mPresContext
)) {
155 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
156 if (NS_WARN_IF(!presShell
)) {
160 nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell
, mEvent
, nullptr,
165 nsIntPoint
UIEvent::GetLayerPoint() const {
166 if (mEvent
->mFlags
.mIsPositionless
) {
167 return nsIntPoint(0, 0);
171 (mEvent
->mClass
!= eMouseEventClass
&&
172 mEvent
->mClass
!= eMouseScrollEventClass
&&
173 mEvent
->mClass
!= eWheelEventClass
&&
174 mEvent
->mClass
!= ePointerEventClass
&&
175 mEvent
->mClass
!= eTouchEventClass
&&
176 mEvent
->mClass
!= eDragEventClass
&&
177 mEvent
->mClass
!= eSimpleGestureEventClass
) ||
178 !mPresContext
|| mEventIsInternal
) {
181 // XXX I'm not really sure this is correct; it's my best shot, though
182 nsIFrame
* targetFrame
= mPresContext
->EventStateManager()->GetEventTarget();
183 if (!targetFrame
) return mLayerPoint
;
184 nsIFrame
* layer
= nsLayoutUtils::GetClosestLayer(targetFrame
);
186 nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent
, RelativeTo
{layer
}));
187 return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt
.x
),
188 nsPresContext::AppUnitsToIntCSSPixels(pt
.y
));
191 void UIEvent::DuplicatePrivateData() {
192 mDefaultClientPoint
= Event::GetClientCoords(
193 mPresContext
, mEvent
, mEvent
->mRefPoint
, mDefaultClientPoint
);
194 mMovementPoint
= GetMovementPoint();
195 mLayerPoint
= GetLayerPoint();
196 mPagePoint
= Event::GetPageCoords(mPresContext
, mEvent
, mEvent
->mRefPoint
,
197 mDefaultClientPoint
);
198 // GetScreenPoint converts mEvent->mRefPoint to right coordinates.
199 CSSIntPoint screenPoint
=
200 Event::GetScreenCoords(mPresContext
, mEvent
, mEvent
->mRefPoint
)
201 .valueOr(CSSIntPoint
{0, 0});
203 Event::DuplicatePrivateData();
205 CSSToLayoutDeviceScale scale
= mPresContext
206 ? mPresContext
->CSSToDevPixelScale()
207 : CSSToLayoutDeviceScale(1);
208 mEvent
->mRefPoint
= RoundedToInt(screenPoint
* scale
);
211 void UIEvent::Serialize(IPC::MessageWriter
* aWriter
,
212 bool aSerializeInterfaceType
) {
213 if (aSerializeInterfaceType
) {
214 IPC::WriteParam(aWriter
, u
"uievent"_ns
);
217 Event::Serialize(aWriter
, false);
219 IPC::WriteParam(aWriter
, Detail());
222 bool UIEvent::Deserialize(IPC::MessageReader
* aReader
) {
223 NS_ENSURE_TRUE(Event::Deserialize(aReader
), false);
224 NS_ENSURE_TRUE(IPC::ReadParam(aReader
, &mDetail
), false);
228 // XXX Following struct and array are used only in
229 // UIEvent::ComputeModifierState(), but if we define them in it,
230 // we fail to build on Mac at calling mozilla::ArrayLength().
231 struct ModifierPair
{
235 static const ModifierPair kPairs
[] = {
237 { MODIFIER_ALT
, NS_DOM_KEYNAME_ALT
},
238 { MODIFIER_ALTGRAPH
, NS_DOM_KEYNAME_ALTGRAPH
},
239 { MODIFIER_CAPSLOCK
, NS_DOM_KEYNAME_CAPSLOCK
},
240 { MODIFIER_CONTROL
, NS_DOM_KEYNAME_CONTROL
},
241 { MODIFIER_FN
, NS_DOM_KEYNAME_FN
},
242 { MODIFIER_FNLOCK
, NS_DOM_KEYNAME_FNLOCK
},
243 { MODIFIER_META
, NS_DOM_KEYNAME_META
},
244 { MODIFIER_NUMLOCK
, NS_DOM_KEYNAME_NUMLOCK
},
245 { MODIFIER_SCROLLLOCK
, NS_DOM_KEYNAME_SCROLLLOCK
},
246 { MODIFIER_SHIFT
, NS_DOM_KEYNAME_SHIFT
},
247 { MODIFIER_SYMBOL
, NS_DOM_KEYNAME_SYMBOL
},
248 { MODIFIER_SYMBOLLOCK
, NS_DOM_KEYNAME_SYMBOLLOCK
},
253 Modifiers
UIEvent::ComputeModifierState(const nsAString
& aModifiersList
) {
254 if (aModifiersList
.IsEmpty()) {
258 // Be careful about the performance. If aModifiersList is too long,
259 // parsing it needs too long time.
260 // XXX Should we abort if aModifiersList is too long?
262 Modifiers modifiers
= 0;
264 nsAString::const_iterator listStart
, listEnd
;
265 aModifiersList
.BeginReading(listStart
);
266 aModifiersList
.EndReading(listEnd
);
268 for (uint32_t i
= 0; i
< ArrayLength(kPairs
); i
++) {
269 nsAString::const_iterator
start(listStart
), end(listEnd
);
270 if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs
[i
].name
), start
, end
)) {
274 if ((start
!= listStart
&& !NS_IsAsciiWhitespace(*(--start
))) ||
275 (end
!= listEnd
&& !NS_IsAsciiWhitespace(*(end
)))) {
278 modifiers
|= kPairs
[i
].modifier
;
284 bool UIEvent::GetModifierStateInternal(const nsAString
& aKey
) {
285 WidgetInputEvent
* inputEvent
= mEvent
->AsInputEvent();
286 MOZ_ASSERT(inputEvent
, "mEvent must be WidgetInputEvent or derived class");
287 return ((inputEvent
->mModifiers
& WidgetInputEvent::GetModifier(aKey
)) != 0);
290 void UIEvent::InitModifiers(const EventModifierInit
& aParam
) {
291 if (NS_WARN_IF(!mEvent
)) {
294 WidgetInputEvent
* inputEvent
= mEvent
->AsInputEvent();
295 MOZ_ASSERT(inputEvent
,
296 "This method shouldn't be called if it doesn't have modifiers");
297 if (NS_WARN_IF(!inputEvent
)) {
301 inputEvent
->mModifiers
= MODIFIER_NONE
;
303 #define SET_MODIFIER(aName, aValue) \
304 if (aParam.m##aName) { \
305 inputEvent->mModifiers |= aValue; \
308 SET_MODIFIER(CtrlKey
, MODIFIER_CONTROL
)
309 SET_MODIFIER(ShiftKey
, MODIFIER_SHIFT
)
310 SET_MODIFIER(AltKey
, MODIFIER_ALT
)
311 SET_MODIFIER(MetaKey
, MODIFIER_META
)
312 SET_MODIFIER(ModifierAltGraph
, MODIFIER_ALTGRAPH
)
313 SET_MODIFIER(ModifierCapsLock
, MODIFIER_CAPSLOCK
)
314 SET_MODIFIER(ModifierFn
, MODIFIER_FN
)
315 SET_MODIFIER(ModifierFnLock
, MODIFIER_FNLOCK
)
316 SET_MODIFIER(ModifierNumLock
, MODIFIER_NUMLOCK
)
317 SET_MODIFIER(ModifierScrollLock
, MODIFIER_SCROLLLOCK
)
318 SET_MODIFIER(ModifierSymbol
, MODIFIER_SYMBOL
)
319 SET_MODIFIER(ModifierSymbolLock
, MODIFIER_SYMBOLLOCK
)
324 } // namespace mozilla::dom
326 using namespace mozilla
;
327 using namespace mozilla::dom
;
329 already_AddRefed
<UIEvent
> NS_NewDOMUIEvent(EventTarget
* aOwner
,
330 nsPresContext
* aPresContext
,
331 WidgetGUIEvent
* aEvent
) {
332 RefPtr
<UIEvent
> it
= new UIEvent(aOwner
, aPresContext
, aEvent
);