UIEvent.cpp (10361B)
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 "mozilla/dom/UIEvent.h" 8 9 #include "base/basictypes.h" 10 #include "ipc/IPCMessageUtils.h" 11 #include "ipc/IPCMessageUtilsSpecializations.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 "nsIDocShell.h" 22 #include "nsIFrame.h" 23 #include "nsIInterfaceRequestorUtils.h" 24 #include "nsLayoutUtils.h" 25 #include "prtime.h" 26 27 namespace mozilla::dom { 28 29 UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext, 30 WidgetGUIEvent* aEvent) 31 : Event(aOwner, aPresContext, 32 aEvent ? aEvent : new InternalUIEvent(false, eVoidEvent, nullptr)), 33 mLayerPoint(0, 0) { 34 if (aEvent) { 35 mEventIsInternal = false; 36 } else { 37 mEventIsInternal = true; 38 } 39 40 // Fill mDetail and mView according to the mEvent (widget-generated 41 // event) we've got 42 switch (mEvent->mClass) { 43 case eUIEventClass: { 44 mDetail = mEvent->AsUIEvent()->mDetail; 45 break; 46 } 47 48 case eScrollPortEventClass: { 49 InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent(); 50 mDetail = static_cast<int32_t>(scrollEvent->mOrient); 51 break; 52 } 53 54 default: 55 mDetail = 0; 56 break; 57 } 58 59 mView = nullptr; 60 if (mPresContext) { 61 nsIDocShell* docShell = mPresContext->GetDocShell(); 62 if (docShell) { 63 mView = docShell->GetWindow(); 64 } 65 } 66 } 67 68 // static 69 already_AddRefed<UIEvent> UIEvent::Constructor(const GlobalObject& aGlobal, 70 const nsAString& aType, 71 const UIEventInit& aParam) { 72 nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); 73 RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr); 74 bool trusted = e->Init(t); 75 e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, 76 aParam.mDetail); 77 e->SetTrusted(trusted); 78 e->SetComposed(aParam.mComposed); 79 return e.forget(); 80 } 81 82 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, mView) 83 84 NS_IMPL_ADDREF_INHERITED(UIEvent, Event) 85 NS_IMPL_RELEASE_INHERITED(UIEvent, Event) 86 87 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent) 88 NS_INTERFACE_MAP_END_INHERITING(Event) 89 90 void UIEvent::InitUIEvent(const nsAString& typeArg, bool canBubbleArg, 91 bool cancelableArg, nsGlobalWindowInner* viewArg, 92 int32_t detailArg) { 93 if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) { 94 return; 95 } 96 97 Event::InitEvent(typeArg, canBubbleArg, cancelableArg); 98 99 mDetail = detailArg; 100 mView = viewArg ? viewArg->GetOuterWindow() : nullptr; 101 } 102 103 already_AddRefed<nsIContent> UIEvent::GetRangeParentContentAndOffset( 104 int32_t* aOffset) const { 105 if (NS_WARN_IF(!mPresContext)) { 106 return nullptr; 107 } 108 RefPtr<PresShell> presShell = mPresContext->GetPresShell(); 109 if (NS_WARN_IF(!presShell)) { 110 return nullptr; 111 } 112 nsCOMPtr<nsIContent> container; 113 nsLayoutUtils::GetContainerAndOffsetAtEvent( 114 presShell, mEvent, getter_AddRefs(container), aOffset); 115 return container.forget(); 116 } 117 118 int32_t UIEvent::RangeOffset() const { 119 if (NS_WARN_IF(!mPresContext)) { 120 return 0; 121 } 122 RefPtr<PresShell> presShell = mPresContext->GetPresShell(); 123 if (NS_WARN_IF(!presShell)) { 124 return 0; 125 } 126 int32_t offset = 0; 127 nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent, nullptr, 128 &offset); 129 return offset; 130 } 131 132 nsIntPoint UIEvent::GetLayerPoint() const { 133 if (mEvent->mFlags.mIsPositionless) { 134 return nsIntPoint(0, 0); 135 } 136 if (!mEvent || 137 (mEvent->mClass != eMouseEventClass && 138 mEvent->mClass != eMouseScrollEventClass && 139 mEvent->mClass != eWheelEventClass && 140 mEvent->mClass != ePointerEventClass && 141 mEvent->mClass != eTouchEventClass && 142 mEvent->mClass != eDragEventClass && 143 mEvent->mClass != eSimpleGestureEventClass) || 144 !mPresContext || mEventIsInternal) { 145 return mLayerPoint; 146 } 147 nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); 148 if (!targetFrame) { 149 return mLayerPoint; 150 } 151 // NOTE(emilio): This matches Blink to my knowledge, but it's generally not 152 // super-well specified, see https://github.com/w3c/uievents/issues/398 153 RelativeTo root{targetFrame->PresShell()->GetRootFrame()}; 154 const nsPoint rootPoint = 155 nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, root); 156 nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame); 157 nsPoint layerRootPoint{0, 0}; 158 if (nsLayoutUtils::TransformPoint(RelativeTo{layer}, RelativeTo{root}, 159 layerRootPoint) != 160 nsLayoutUtils::TRANSFORM_SUCCEEDED) { 161 return mLayerPoint; 162 } 163 return RoundedToInt(CSSPoint::FromAppUnits(rootPoint - layerRootPoint)) 164 .ToUnknownPoint(); 165 } 166 167 void UIEvent::DuplicatePrivateData() { 168 mLayerPoint = GetLayerPoint(); 169 170 // GetScreenCoords() converts mEvent->mRefPoint to right coordinates. 171 // Note that mPresContext will be cleared by Event::DuplicatePrivateData(). 172 // Therefore, we need to use mPresContext before calling it. 173 const CSSIntPoint screenPoint = RoundedToInt( 174 Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint) 175 .valueOr(CSSIntPoint{0, 0})); 176 177 Event::DuplicatePrivateData(); 178 MOZ_ASSERT_IF(!mEventIsInternal, !mPresContext); 179 180 // GetScreenCoords() has already computed the screen point in CSS pixels which 181 // applied the scale of mPresContext. Additionally, we don't have the widget 182 // anymore. Therefore, we need to cache the point as in the screen 183 // coordinates. 184 MOZ_ASSERT(!mEvent || !mEvent->AsGUIEvent() || 185 !mEvent->AsGUIEvent()->mWidget); 186 mEvent->mRefPoint = RoundedToInt(screenPoint * CSSToLayoutDeviceScale(1)); 187 } 188 189 void UIEvent::Serialize(IPC::MessageWriter* aWriter, 190 bool aSerializeInterfaceType) { 191 if (aSerializeInterfaceType) { 192 IPC::WriteParam(aWriter, u"uievent"_ns); 193 } 194 195 Event::Serialize(aWriter, false); 196 197 IPC::WriteParam(aWriter, Detail()); 198 } 199 200 bool UIEvent::Deserialize(IPC::MessageReader* aReader) { 201 NS_ENSURE_TRUE(Event::Deserialize(aReader), false); 202 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &mDetail), false); 203 return true; 204 } 205 206 // XXX Following struct and array are used only in 207 // UIEvent::ComputeModifierState(), but if we define them in it, 208 // we fail to build on Mac at calling std::size(). 209 struct ModifierPair { 210 Modifier modifier; 211 const char* name; 212 }; 213 static const ModifierPair kPairs[] = { 214 // clang-format off 215 { MODIFIER_ALT, NS_DOM_KEYNAME_ALT }, 216 { MODIFIER_ALTGRAPH, NS_DOM_KEYNAME_ALTGRAPH }, 217 { MODIFIER_CAPSLOCK, NS_DOM_KEYNAME_CAPSLOCK }, 218 { MODIFIER_CONTROL, NS_DOM_KEYNAME_CONTROL }, 219 { MODIFIER_FN, NS_DOM_KEYNAME_FN }, 220 { MODIFIER_FNLOCK, NS_DOM_KEYNAME_FNLOCK }, 221 { MODIFIER_META, NS_DOM_KEYNAME_META }, 222 { MODIFIER_NUMLOCK, NS_DOM_KEYNAME_NUMLOCK }, 223 { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK }, 224 { MODIFIER_SHIFT, NS_DOM_KEYNAME_SHIFT }, 225 { MODIFIER_SYMBOL, NS_DOM_KEYNAME_SYMBOL }, 226 { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK }, 227 // clang-format on 228 }; 229 230 // static 231 Modifiers UIEvent::ComputeModifierState(const nsAString& aModifiersList) { 232 if (aModifiersList.IsEmpty()) { 233 return 0; 234 } 235 236 // Be careful about the performance. If aModifiersList is too long, 237 // parsing it needs too long time. 238 // XXX Should we abort if aModifiersList is too long? 239 240 Modifiers modifiers = 0; 241 242 nsAString::const_iterator listStart, listEnd; 243 aModifiersList.BeginReading(listStart); 244 aModifiersList.EndReading(listEnd); 245 246 for (uint32_t i = 0; i < std::size(kPairs); i++) { 247 nsAString::const_iterator start(listStart), end(listEnd); 248 if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs[i].name), start, end)) { 249 continue; 250 } 251 252 if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) || 253 (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) { 254 continue; 255 } 256 modifiers |= kPairs[i].modifier; 257 } 258 259 return modifiers; 260 } 261 262 bool UIEvent::GetModifierStateInternal(const nsAString& aKey) { 263 WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); 264 MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class"); 265 return ((inputEvent->mModifiers & WidgetInputEvent::GetModifier(aKey)) != 0); 266 } 267 268 static Modifiers ConvertToModifiers(const EventModifierInit& aParam) { 269 Modifiers bits = MODIFIER_NONE; 270 271 #define SET_MODIFIER(aName, aValue) bits |= aParam.m##aName ? (aValue) : 0; 272 273 SET_MODIFIER(CtrlKey, MODIFIER_CONTROL) 274 SET_MODIFIER(ShiftKey, MODIFIER_SHIFT) 275 SET_MODIFIER(AltKey, MODIFIER_ALT) 276 SET_MODIFIER(MetaKey, MODIFIER_META) 277 SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH) 278 SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK) 279 SET_MODIFIER(ModifierFn, MODIFIER_FN) 280 SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK) 281 SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK) 282 SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK) 283 SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL) 284 SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK) 285 286 #undef SET_MODIFIER 287 288 return bits; 289 } 290 291 void UIEvent::InitModifiers(const EventModifierInit& aParam) { 292 if (NS_WARN_IF(!mEvent)) { 293 return; 294 } 295 WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); 296 MOZ_ASSERT(inputEvent, 297 "This method shouldn't be called if it doesn't have modifiers"); 298 if (NS_WARN_IF(!inputEvent)) { 299 return; 300 } 301 302 inputEvent->mModifiers = ConvertToModifiers(aParam); 303 } 304 305 } // namespace mozilla::dom 306 307 using namespace mozilla; 308 using namespace mozilla::dom; 309 310 already_AddRefed<UIEvent> NS_NewDOMUIEvent(EventTarget* aOwner, 311 nsPresContext* aPresContext, 312 WidgetGUIEvent* aEvent) { 313 RefPtr<UIEvent> it = new UIEvent(aOwner, aPresContext, aEvent); 314 return it.forget(); 315 }