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),
36 mMovementPoint(0, 0) {
38 mEventIsInternal
= false;
40 mEventIsInternal
= true;
43 // Fill mDetail and mView according to the mEvent (widget-generated
45 switch (mEvent
->mClass
) {
47 mDetail
= mEvent
->AsUIEvent()->mDetail
;
51 case eScrollPortEventClass
: {
52 InternalScrollPortEvent
* scrollEvent
= mEvent
->AsScrollPortEvent();
53 mDetail
= static_cast<int32_t>(scrollEvent
->mOrient
);
64 nsIDocShell
* docShell
= mPresContext
->GetDocShell();
66 mView
= docShell
->GetWindow();
72 already_AddRefed
<UIEvent
> UIEvent::Constructor(const GlobalObject
& aGlobal
,
73 const nsAString
& aType
,
74 const UIEventInit
& aParam
) {
75 nsCOMPtr
<EventTarget
> t
= do_QueryInterface(aGlobal
.GetAsSupports());
76 RefPtr
<UIEvent
> e
= new UIEvent(t
, nullptr, nullptr);
77 bool trusted
= e
->Init(t
);
78 e
->InitUIEvent(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
80 e
->SetTrusted(trusted
);
81 e
->SetComposed(aParam
.mComposed
);
85 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent
, Event
, mView
)
87 NS_IMPL_ADDREF_INHERITED(UIEvent
, Event
)
88 NS_IMPL_RELEASE_INHERITED(UIEvent
, Event
)
90 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent
)
91 NS_INTERFACE_MAP_END_INHERITING(Event
)
93 static nsIntPoint
DevPixelsToCSSPixels(const LayoutDeviceIntPoint
& aPoint
,
94 nsPresContext
* aContext
) {
95 return nsIntPoint(aContext
->DevPixelsToIntCSSPixels(aPoint
.x
),
96 aContext
->DevPixelsToIntCSSPixels(aPoint
.y
));
99 nsIntPoint
UIEvent::GetMovementPoint() {
100 if (mEvent
->mFlags
.mIsPositionless
) {
101 return nsIntPoint(0, 0);
104 if (mPrivateDataDuplicated
|| mEventIsInternal
) {
105 return mMovementPoint
;
108 if (!mEvent
|| !mEvent
->AsGUIEvent()->mWidget
||
109 (mEvent
->mMessage
!= eMouseMove
&& mEvent
->mMessage
!= ePointerMove
)) {
110 // Pointer Lock spec defines that movementX/Y must be zero for all mouse
111 // events except mousemove.
112 return nsIntPoint(0, 0);
115 // Calculate the delta between the last screen point and the current one.
116 nsIntPoint current
= DevPixelsToCSSPixels(mEvent
->mRefPoint
, mPresContext
);
117 nsIntPoint last
= DevPixelsToCSSPixels(mEvent
->mLastRefPoint
, mPresContext
);
118 return current
- last
;
121 void UIEvent::InitUIEvent(const nsAString
& typeArg
, bool canBubbleArg
,
122 bool cancelableArg
, nsGlobalWindowInner
* viewArg
,
124 if (NS_WARN_IF(mEvent
->mFlags
.mIsBeingDispatched
)) {
128 Event::InitEvent(typeArg
, canBubbleArg
, cancelableArg
);
131 mView
= viewArg
? viewArg
->GetOuterWindow() : nullptr;
134 already_AddRefed
<nsIContent
> UIEvent::GetRangeParentContentAndOffset(
135 int32_t* aOffset
) const {
136 if (NS_WARN_IF(!mPresContext
)) {
139 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
140 if (NS_WARN_IF(!presShell
)) {
143 nsCOMPtr
<nsIContent
> container
;
144 nsLayoutUtils::GetContainerAndOffsetAtEvent(
145 presShell
, mEvent
, getter_AddRefs(container
), aOffset
);
146 return container
.forget();
149 int32_t UIEvent::RangeOffset() const {
150 if (NS_WARN_IF(!mPresContext
)) {
153 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
154 if (NS_WARN_IF(!presShell
)) {
158 nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell
, mEvent
, nullptr,
163 nsIntPoint
UIEvent::GetLayerPoint() const {
164 if (mEvent
->mFlags
.mIsPositionless
) {
165 return nsIntPoint(0, 0);
169 (mEvent
->mClass
!= eMouseEventClass
&&
170 mEvent
->mClass
!= eMouseScrollEventClass
&&
171 mEvent
->mClass
!= eWheelEventClass
&&
172 mEvent
->mClass
!= ePointerEventClass
&&
173 mEvent
->mClass
!= eTouchEventClass
&&
174 mEvent
->mClass
!= eDragEventClass
&&
175 mEvent
->mClass
!= eSimpleGestureEventClass
) ||
176 !mPresContext
|| mEventIsInternal
) {
179 // XXX I'm not really sure this is correct; it's my best shot, though
180 nsIFrame
* targetFrame
= mPresContext
->EventStateManager()->GetEventTarget();
181 if (!targetFrame
) return mLayerPoint
;
182 nsIFrame
* layer
= nsLayoutUtils::GetClosestLayer(targetFrame
);
184 nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent
, RelativeTo
{layer
}));
185 return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt
.x
),
186 nsPresContext::AppUnitsToIntCSSPixels(pt
.y
));
189 void UIEvent::DuplicatePrivateData() {
190 mDefaultClientPoint
= Event::GetClientCoords(
191 mPresContext
, mEvent
, mEvent
->mRefPoint
, mDefaultClientPoint
);
192 mMovementPoint
= GetMovementPoint();
193 mLayerPoint
= GetLayerPoint();
194 mPagePoint
= Event::GetPageCoords(mPresContext
, mEvent
, mEvent
->mRefPoint
,
195 mDefaultClientPoint
);
196 // GetScreenPoint converts mEvent->mRefPoint to right coordinates.
197 CSSIntPoint screenPoint
=
198 Event::GetScreenCoords(mPresContext
, mEvent
, mEvent
->mRefPoint
)
199 .valueOr(CSSIntPoint
{0, 0});
201 Event::DuplicatePrivateData();
203 CSSToLayoutDeviceScale scale
= mPresContext
204 ? mPresContext
->CSSToDevPixelScale()
205 : CSSToLayoutDeviceScale(1);
206 mEvent
->mRefPoint
= RoundedToInt(screenPoint
* scale
);
209 void UIEvent::Serialize(IPC::MessageWriter
* aWriter
,
210 bool aSerializeInterfaceType
) {
211 if (aSerializeInterfaceType
) {
212 IPC::WriteParam(aWriter
, u
"uievent"_ns
);
215 Event::Serialize(aWriter
, false);
217 IPC::WriteParam(aWriter
, Detail());
220 bool UIEvent::Deserialize(IPC::MessageReader
* aReader
) {
221 NS_ENSURE_TRUE(Event::Deserialize(aReader
), false);
222 NS_ENSURE_TRUE(IPC::ReadParam(aReader
, &mDetail
), false);
226 // XXX Following struct and array are used only in
227 // UIEvent::ComputeModifierState(), but if we define them in it,
228 // we fail to build on Mac at calling mozilla::ArrayLength().
229 struct ModifierPair
{
233 static const ModifierPair kPairs
[] = {
235 { MODIFIER_ALT
, NS_DOM_KEYNAME_ALT
},
236 { MODIFIER_ALTGRAPH
, NS_DOM_KEYNAME_ALTGRAPH
},
237 { MODIFIER_CAPSLOCK
, NS_DOM_KEYNAME_CAPSLOCK
},
238 { MODIFIER_CONTROL
, NS_DOM_KEYNAME_CONTROL
},
239 { MODIFIER_FN
, NS_DOM_KEYNAME_FN
},
240 { MODIFIER_FNLOCK
, NS_DOM_KEYNAME_FNLOCK
},
241 { MODIFIER_META
, NS_DOM_KEYNAME_META
},
242 { MODIFIER_NUMLOCK
, NS_DOM_KEYNAME_NUMLOCK
},
243 { MODIFIER_SCROLLLOCK
, NS_DOM_KEYNAME_SCROLLLOCK
},
244 { MODIFIER_SHIFT
, NS_DOM_KEYNAME_SHIFT
},
245 { MODIFIER_SYMBOL
, NS_DOM_KEYNAME_SYMBOL
},
246 { MODIFIER_SYMBOLLOCK
, NS_DOM_KEYNAME_SYMBOLLOCK
},
251 Modifiers
UIEvent::ComputeModifierState(const nsAString
& aModifiersList
) {
252 if (aModifiersList
.IsEmpty()) {
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?
260 Modifiers modifiers
= 0;
262 nsAString::const_iterator listStart
, listEnd
;
263 aModifiersList
.BeginReading(listStart
);
264 aModifiersList
.EndReading(listEnd
);
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
)) {
272 if ((start
!= listStart
&& !NS_IsAsciiWhitespace(*(--start
))) ||
273 (end
!= listEnd
&& !NS_IsAsciiWhitespace(*(end
)))) {
276 modifiers
|= kPairs
[i
].modifier
;
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);
288 static Modifiers
ConvertToModifiers(const EventModifierInit
& aParam
) {
289 Modifiers bits
= MODIFIER_NONE
;
291 #define SET_MODIFIER(aName, aValue) bits |= aParam.m##aName ? (aValue) : 0;
293 SET_MODIFIER(CtrlKey
, MODIFIER_CONTROL
)
294 SET_MODIFIER(ShiftKey
, MODIFIER_SHIFT
)
295 SET_MODIFIER(AltKey
, MODIFIER_ALT
)
296 SET_MODIFIER(MetaKey
, MODIFIER_META
)
297 SET_MODIFIER(ModifierAltGraph
, MODIFIER_ALTGRAPH
)
298 SET_MODIFIER(ModifierCapsLock
, MODIFIER_CAPSLOCK
)
299 SET_MODIFIER(ModifierFn
, MODIFIER_FN
)
300 SET_MODIFIER(ModifierFnLock
, MODIFIER_FNLOCK
)
301 SET_MODIFIER(ModifierNumLock
, MODIFIER_NUMLOCK
)
302 SET_MODIFIER(ModifierScrollLock
, MODIFIER_SCROLLLOCK
)
303 SET_MODIFIER(ModifierSymbol
, MODIFIER_SYMBOL
)
304 SET_MODIFIER(ModifierSymbolLock
, MODIFIER_SYMBOLLOCK
)
311 void UIEvent::InitModifiers(const EventModifierInit
& aParam
) {
312 if (NS_WARN_IF(!mEvent
)) {
315 WidgetInputEvent
* inputEvent
= mEvent
->AsInputEvent();
316 MOZ_ASSERT(inputEvent
,
317 "This method shouldn't be called if it doesn't have modifiers");
318 if (NS_WARN_IF(!inputEvent
)) {
322 inputEvent
->mModifiers
= ConvertToModifiers(aParam
);
325 } // namespace mozilla::dom
327 using namespace mozilla
;
328 using namespace mozilla::dom
;
330 already_AddRefed
<UIEvent
> NS_NewDOMUIEvent(EventTarget
* aOwner
,
331 nsPresContext
* aPresContext
,
332 WidgetGUIEvent
* aEvent
) {
333 RefPtr
<UIEvent
> it
= new UIEvent(aOwner
, aPresContext
, aEvent
);