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/PresShell.h"
15 #include "mozilla/TextEvents.h"
16 #include "nsCOMPtr.h"
17 #include "nsContentUtils.h"
18 #include "nsIContent.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsIDocShell.h"
21 #include "nsIFrame.h"
22 #include "prtime.h"
23 
24 namespace mozilla {
25 namespace dom {
26 
UIEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetGUIEvent * aEvent)27 UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext,
28                  WidgetGUIEvent* aEvent)
29     : Event(aOwner, aPresContext,
30             aEvent ? aEvent : new InternalUIEvent(false, eVoidEvent, nullptr)),
31       mClientPoint(0, 0),
32       mLayerPoint(0, 0),
33       mPagePoint(0, 0),
34       mMovementPoint(0, 0),
35       mIsPointerLocked(EventStateManager::sIsPointerLocked),
36       mLastClientPoint(EventStateManager::sLastClientPoint) {
37   if (aEvent) {
38     mEventIsInternal = false;
39   } else {
40     mEventIsInternal = true;
41     mEvent->mTime = PR_Now();
42   }
43 
44   // Fill mDetail and mView according to the mEvent (widget-generated
45   // event) we've got
46   switch (mEvent->mClass) {
47     case eUIEventClass: {
48       mDetail = mEvent->AsUIEvent()->mDetail;
49       break;
50     }
51 
52     case eScrollPortEventClass: {
53       InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent();
54       mDetail = static_cast<int32_t>(scrollEvent->mOrient);
55       break;
56     }
57 
58     default:
59       mDetail = 0;
60       break;
61   }
62 
63   mView = nullptr;
64   if (mPresContext) {
65     nsIDocShell* docShell = mPresContext->GetDocShell();
66     if (docShell) {
67       mView = docShell->GetWindow();
68     }
69   }
70 }
71 
72 // static
Constructor(const GlobalObject & aGlobal,const nsAString & aType,const UIEventInit & aParam)73 already_AddRefed<UIEvent> UIEvent::Constructor(const GlobalObject& aGlobal,
74                                                const nsAString& aType,
75                                                const UIEventInit& aParam) {
76   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
77   RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr);
78   bool trusted = e->Init(t);
79   e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
80                  aParam.mDetail);
81   e->SetTrusted(trusted);
82   e->SetComposed(aParam.mComposed);
83   return e.forget();
84 }
85 
NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent,Event,mView)86 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, mView)
87 
88 NS_IMPL_ADDREF_INHERITED(UIEvent, Event)
89 NS_IMPL_RELEASE_INHERITED(UIEvent, Event)
90 
91 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent)
92 NS_INTERFACE_MAP_END_INHERITING(Event)
93 
94 static nsIntPoint DevPixelsToCSSPixels(const LayoutDeviceIntPoint& aPoint,
95                                        nsPresContext* aContext) {
96   return nsIntPoint(aContext->DevPixelsToIntCSSPixels(aPoint.x),
97                     aContext->DevPixelsToIntCSSPixels(aPoint.y));
98 }
99 
GetMovementPoint()100 nsIntPoint UIEvent::GetMovementPoint() {
101   if (mEvent->mFlags.mIsPositionless) {
102     return nsIntPoint(0, 0);
103   }
104 
105   if (mPrivateDataDuplicated || mEventIsInternal) {
106     return mMovementPoint;
107   }
108 
109   if (!mEvent || !mEvent->AsGUIEvent()->mWidget ||
110       (mEvent->mMessage != eMouseMove && mEvent->mMessage != ePointerMove)) {
111     // Pointer Lock spec defines that movementX/Y must be zero for all mouse
112     // events except mousemove.
113     return nsIntPoint(0, 0);
114   }
115 
116   // Calculate the delta between the last screen point and the current one.
117   nsIntPoint current = DevPixelsToCSSPixels(mEvent->mRefPoint, mPresContext);
118   nsIntPoint last = DevPixelsToCSSPixels(mEvent->mLastRefPoint, mPresContext);
119   return current - last;
120 }
121 
InitUIEvent(const nsAString & typeArg,bool canBubbleArg,bool cancelableArg,nsGlobalWindowInner * viewArg,int32_t detailArg)122 void UIEvent::InitUIEvent(const nsAString& typeArg, bool canBubbleArg,
123                           bool cancelableArg, nsGlobalWindowInner* viewArg,
124                           int32_t detailArg) {
125   if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) {
126     return;
127   }
128 
129   Event::InitEvent(typeArg, canBubbleArg, cancelableArg);
130 
131   mDetail = detailArg;
132   mView = viewArg ? viewArg->GetOuterWindow() : nullptr;
133 }
134 
GetRangeParentContentAndOffset(int32_t * aOffset)135 already_AddRefed<nsIContent> UIEvent::GetRangeParentContentAndOffset(
136     int32_t* aOffset) {
137   if (NS_WARN_IF(!mPresContext)) {
138     return nullptr;
139   }
140   RefPtr<PresShell> presShell = mPresContext->GetPresShell();
141   if (NS_WARN_IF(!presShell)) {
142     return nullptr;
143   }
144   nsCOMPtr<nsIContent> container;
145   nsLayoutUtils::GetContainerAndOffsetAtEvent(
146       presShell, mEvent, getter_AddRefs(container), aOffset);
147   return container.forget();
148 }
149 
RangeOffset() const150 int32_t UIEvent::RangeOffset() const {
151   if (NS_WARN_IF(!mPresContext)) {
152     return 0;
153   }
154   RefPtr<PresShell> presShell = mPresContext->GetPresShell();
155   if (NS_WARN_IF(!presShell)) {
156     return 0;
157   }
158   int32_t offset = 0;
159   nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent, nullptr,
160                                               &offset);
161   return offset;
162 }
163 
GetLayerPoint() const164 nsIntPoint UIEvent::GetLayerPoint() const {
165   if (mEvent->mFlags.mIsPositionless) {
166     return nsIntPoint(0, 0);
167   }
168 
169   if (!mEvent ||
170       (mEvent->mClass != eMouseEventClass &&
171        mEvent->mClass != eMouseScrollEventClass &&
172        mEvent->mClass != eWheelEventClass &&
173        mEvent->mClass != ePointerEventClass &&
174        mEvent->mClass != eTouchEventClass &&
175        mEvent->mClass != eDragEventClass &&
176        mEvent->mClass != eSimpleGestureEventClass) ||
177       !mPresContext || mEventIsInternal) {
178     return mLayerPoint;
179   }
180   // XXX I'm not really sure this is correct; it's my best shot, though
181   nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
182   if (!targetFrame) return mLayerPoint;
183   nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame);
184   nsPoint pt(
185       nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, RelativeTo{layer}));
186   return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
187                     nsPresContext::AppUnitsToIntCSSPixels(pt.y));
188 }
189 
DuplicatePrivateData()190 void UIEvent::DuplicatePrivateData() {
191   mClientPoint = Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint,
192                                         mClientPoint);
193   mMovementPoint = GetMovementPoint();
194   mLayerPoint = GetLayerPoint();
195   mPagePoint = Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint,
196                                     mClientPoint);
197   // GetScreenPoint converts mEvent->mRefPoint to right coordinates.
198   CSSIntPoint screenPoint =
199       Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint);
200 
201   Event::DuplicatePrivateData();
202 
203   CSSToLayoutDeviceScale scale = mPresContext
204                                      ? mPresContext->CSSToDevPixelScale()
205                                      : CSSToLayoutDeviceScale(1);
206   mEvent->mRefPoint = RoundedToInt(screenPoint * scale);
207 }
208 
Serialize(IPC::Message * aMsg,bool aSerializeInterfaceType)209 void UIEvent::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) {
210   if (aSerializeInterfaceType) {
211     IPC::WriteParam(aMsg, NS_LITERAL_STRING("uievent"));
212   }
213 
214   Event::Serialize(aMsg, false);
215 
216   IPC::WriteParam(aMsg, Detail());
217 }
218 
Deserialize(const IPC::Message * aMsg,PickleIterator * aIter)219 bool UIEvent::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter) {
220   NS_ENSURE_TRUE(Event::Deserialize(aMsg, aIter), false);
221   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &mDetail), false);
222   return true;
223 }
224 
225 // XXX Following struct and array are used only in
226 //     UIEvent::ComputeModifierState(), but if we define them in it,
227 //     we fail to build on Mac at calling mozilla::ArrayLength().
228 struct ModifierPair {
229   Modifier modifier;
230   const char* name;
231 };
232 static const ModifierPair kPairs[] = {
233     // clang-format off
234   { MODIFIER_ALT,        NS_DOM_KEYNAME_ALT },
235   { MODIFIER_ALTGRAPH,   NS_DOM_KEYNAME_ALTGRAPH },
236   { MODIFIER_CAPSLOCK,   NS_DOM_KEYNAME_CAPSLOCK },
237   { MODIFIER_CONTROL,    NS_DOM_KEYNAME_CONTROL },
238   { MODIFIER_FN,         NS_DOM_KEYNAME_FN },
239   { MODIFIER_FNLOCK,     NS_DOM_KEYNAME_FNLOCK },
240   { MODIFIER_META,       NS_DOM_KEYNAME_META },
241   { MODIFIER_NUMLOCK,    NS_DOM_KEYNAME_NUMLOCK },
242   { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK },
243   { MODIFIER_SHIFT,      NS_DOM_KEYNAME_SHIFT },
244   { MODIFIER_SYMBOL,     NS_DOM_KEYNAME_SYMBOL },
245   { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK },
246   { MODIFIER_OS,         NS_DOM_KEYNAME_OS }
247     // clang-format on
248 };
249 
250 // static
ComputeModifierState(const nsAString & aModifiersList)251 Modifiers UIEvent::ComputeModifierState(const nsAString& aModifiersList) {
252   if (aModifiersList.IsEmpty()) {
253     return 0;
254   }
255 
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?
259 
260   Modifiers modifiers = 0;
261 
262   nsAString::const_iterator listStart, listEnd;
263   aModifiersList.BeginReading(listStart);
264   aModifiersList.EndReading(listEnd);
265 
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;
270     }
271 
272     if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) ||
273         (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) {
274       continue;
275     }
276     modifiers |= kPairs[i].modifier;
277   }
278 
279   return modifiers;
280 }
281 
GetModifierStateInternal(const nsAString & aKey)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);
286 }
287 
InitModifiers(const EventModifierInit & aParam)288 void UIEvent::InitModifiers(const EventModifierInit& aParam) {
289   if (NS_WARN_IF(!mEvent)) {
290     return;
291   }
292   WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
293   MOZ_ASSERT(inputEvent,
294              "This method shouldn't be called if it doesn't have modifiers");
295   if (NS_WARN_IF(!inputEvent)) {
296     return;
297   }
298 
299   inputEvent->mModifiers = MODIFIER_NONE;
300 
301 #define SET_MODIFIER(aName, aValue)   \
302   if (aParam.m##aName) {              \
303     inputEvent->mModifiers |= aValue; \
304   }
305 
306   SET_MODIFIER(CtrlKey, MODIFIER_CONTROL)
307   SET_MODIFIER(ShiftKey, MODIFIER_SHIFT)
308   SET_MODIFIER(AltKey, MODIFIER_ALT)
309   SET_MODIFIER(MetaKey, MODIFIER_META)
310   SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH)
311   SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK)
312   SET_MODIFIER(ModifierFn, MODIFIER_FN)
313   SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK)
314   SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK)
315   SET_MODIFIER(ModifierOS, MODIFIER_OS)
316   SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK)
317   SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL)
318   SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK)
319 
320 #undef SET_MODIFIER
321 }
322 
323 }  // namespace dom
324 }  // namespace mozilla
325 
326 using namespace mozilla;
327 using namespace mozilla::dom;
328 
NS_NewDOMUIEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetGUIEvent * aEvent)329 already_AddRefed<UIEvent> NS_NewDOMUIEvent(EventTarget* aOwner,
330                                            nsPresContext* aPresContext,
331                                            WidgetGUIEvent* aEvent) {
332   RefPtr<UIEvent> it = new UIEvent(aOwner, aPresContext, aEvent);
333   return it.forget();
334 }
335