Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / events / UIEvent.cpp
blobce43eb08f5967feaf5b3185db71ef26787304d2a
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"
18 #include "nsCOMPtr.h"
19 #include "nsContentUtils.h"
20 #include "nsIContent.h"
21 #include "nsIInterfaceRequestorUtils.h"
22 #include "nsIDocShell.h"
23 #include "nsIFrame.h"
24 #include "nsLayoutUtils.h"
25 #include "prtime.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),
34 mLayerPoint(0, 0),
35 mPagePoint(0, 0),
36 mMovementPoint(0, 0) {
37 if (aEvent) {
38 mEventIsInternal = false;
39 } else {
40 mEventIsInternal = true;
43 // Fill mDetail and mView according to the mEvent (widget-generated
44 // event) we've got
45 switch (mEvent->mClass) {
46 case eUIEventClass: {
47 mDetail = mEvent->AsUIEvent()->mDetail;
48 break;
51 case eScrollPortEventClass: {
52 InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent();
53 mDetail = static_cast<int32_t>(scrollEvent->mOrient);
54 break;
57 default:
58 mDetail = 0;
59 break;
62 mView = nullptr;
63 if (mPresContext) {
64 nsIDocShell* docShell = mPresContext->GetDocShell();
65 if (docShell) {
66 mView = docShell->GetWindow();
71 // static
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,
79 aParam.mDetail);
80 e->SetTrusted(trusted);
81 e->SetComposed(aParam.mComposed);
82 return e.forget();
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,
123 int32_t detailArg) {
124 if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) {
125 return;
128 Event::InitEvent(typeArg, canBubbleArg, cancelableArg);
130 mDetail = detailArg;
131 mView = viewArg ? viewArg->GetOuterWindow() : nullptr;
134 already_AddRefed<nsIContent> UIEvent::GetRangeParentContentAndOffset(
135 int32_t* aOffset) const {
136 if (NS_WARN_IF(!mPresContext)) {
137 return nullptr;
139 RefPtr<PresShell> presShell = mPresContext->GetPresShell();
140 if (NS_WARN_IF(!presShell)) {
141 return nullptr;
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)) {
151 return 0;
153 RefPtr<PresShell> presShell = mPresContext->GetPresShell();
154 if (NS_WARN_IF(!presShell)) {
155 return 0;
157 int32_t offset = 0;
158 nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent, nullptr,
159 &offset);
160 return offset;
163 nsIntPoint UIEvent::GetLayerPoint() const {
164 if (mEvent->mFlags.mIsPositionless) {
165 return nsIntPoint(0, 0);
168 if (!mEvent ||
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) {
177 return mLayerPoint;
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);
183 nsPoint pt(
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);
223 return true;
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 {
230 Modifier modifier;
231 const char* name;
233 static const ModifierPair kPairs[] = {
234 // clang-format off
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 },
247 // clang-format on
250 // static
251 Modifiers UIEvent::ComputeModifierState(const nsAString& aModifiersList) {
252 if (aModifiersList.IsEmpty()) {
253 return 0;
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)) {
269 continue;
272 if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) ||
273 (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) {
274 continue;
276 modifiers |= kPairs[i].modifier;
279 return modifiers;
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)
306 #undef SET_MODIFIER
308 return bits;
311 void UIEvent::InitModifiers(const EventModifierInit& aParam) {
312 if (NS_WARN_IF(!mEvent)) {
313 return;
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)) {
319 return;
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);
334 return it.forget();