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/KeyboardEvent.h"
8 #include "mozilla/TextEvents.h"
9 #include "nsContentUtils.h"
10 #include "prtime.h"
11 
12 namespace mozilla {
13 namespace dom {
14 
KeyboardEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetKeyboardEvent * aEvent)15 KeyboardEvent::KeyboardEvent(EventTarget* aOwner, nsPresContext* aPresContext,
16                              WidgetKeyboardEvent* aEvent)
17     : UIEvent(aOwner, aPresContext,
18               aEvent ? aEvent
19                      : new WidgetKeyboardEvent(false, eVoidEvent, nullptr)),
20       mInitializedByCtor(false),
21       mInitializedWhichValue(0) {
22   if (aEvent) {
23     mEventIsInternal = false;
24   } else {
25     mEventIsInternal = true;
26     mEvent->mTime = PR_Now();
27     mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
28   }
29 }
30 
AltKey(CallerType aCallerType)31 bool KeyboardEvent::AltKey(CallerType aCallerType) {
32   bool altState = mEvent->AsKeyboardEvent()->IsAlt();
33 
34   if (!ShouldResistFingerprinting(aCallerType)) {
35     return altState;
36   }
37 
38   // We need to give a spoofed state for Alt key since it could be used as a
39   // modifier key in certain keyboard layout. For example, the '@' key for
40   // German keyboard for MAC is Alt+L.
41   return GetSpoofedModifierStates(Modifier::MODIFIER_ALT, altState);
42 }
43 
CtrlKey(CallerType aCallerType)44 bool KeyboardEvent::CtrlKey(CallerType aCallerType) {
45   // We don't spoof this key when privacy.resistFingerprinting
46   // is enabled, because it is often used for command key
47   // combinations in web apps.
48   return mEvent->AsKeyboardEvent()->IsControl();
49 }
50 
ShiftKey(CallerType aCallerType)51 bool KeyboardEvent::ShiftKey(CallerType aCallerType) {
52   bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
53 
54   if (!ShouldResistFingerprinting(aCallerType)) {
55     return shiftState;
56   }
57 
58   return GetSpoofedModifierStates(Modifier::MODIFIER_SHIFT, shiftState);
59 }
60 
MetaKey()61 bool KeyboardEvent::MetaKey() {
62   // We don't spoof this key when privacy.resistFingerprinting
63   // is enabled, because it is often used for command key
64   // combinations in web apps.
65   return mEvent->AsKeyboardEvent()->IsMeta();
66 }
67 
Repeat()68 bool KeyboardEvent::Repeat() { return mEvent->AsKeyboardEvent()->mIsRepeat; }
69 
IsComposing()70 bool KeyboardEvent::IsComposing() {
71   return mEvent->AsKeyboardEvent()->mIsComposing;
72 }
73 
GetKey(nsAString & aKeyName) const74 void KeyboardEvent::GetKey(nsAString& aKeyName) const {
75   mEvent->AsKeyboardEvent()->GetDOMKeyName(aKeyName);
76 }
77 
GetCode(nsAString & aCodeName,CallerType aCallerType)78 void KeyboardEvent::GetCode(nsAString& aCodeName, CallerType aCallerType) {
79   if (!ShouldResistFingerprinting(aCallerType)) {
80     mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
81     return;
82   }
83 
84   // When fingerprinting resistance is enabled, we will give a spoofed code
85   // according to the content-language of the document.
86   nsCOMPtr<nsIDocument> doc = GetDocument();
87 
88   nsRFPService::GetSpoofedCode(doc, mEvent->AsKeyboardEvent(), aCodeName);
89 }
90 
GetInitDict(KeyboardEventInit & aParam)91 void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam) {
92   GetKey(aParam.mKey);
93   GetCode(aParam.mCode);
94   aParam.mLocation = Location();
95   aParam.mRepeat = Repeat();
96   aParam.mIsComposing = IsComposing();
97 
98   // legacy attributes
99   aParam.mKeyCode = KeyCode();
100   aParam.mCharCode = CharCode();
101   aParam.mWhich = Which();
102 
103   // modifiers from EventModifierInit
104   aParam.mCtrlKey = CtrlKey();
105   aParam.mShiftKey = ShiftKey();
106   aParam.mAltKey = AltKey();
107   aParam.mMetaKey = MetaKey();
108 
109   WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
110   aParam.mModifierAltGraph = internalEvent->IsAltGraph();
111   aParam.mModifierCapsLock = internalEvent->IsCapsLocked();
112   aParam.mModifierFn = internalEvent->IsFn();
113   aParam.mModifierFnLock = internalEvent->IsFnLocked();
114   aParam.mModifierNumLock = internalEvent->IsNumLocked();
115   aParam.mModifierOS = internalEvent->IsOS();
116   aParam.mModifierScrollLock = internalEvent->IsScrollLocked();
117   aParam.mModifierSymbol = internalEvent->IsSymbol();
118   aParam.mModifierSymbolLock = internalEvent->IsSymbolLocked();
119 
120   // EventInit
121   aParam.mBubbles = internalEvent->mFlags.mBubbles;
122   aParam.mCancelable = internalEvent->mFlags.mCancelable;
123 }
124 
CharCode()125 uint32_t KeyboardEvent::CharCode() {
126   // If this event is initialized with ctor, we shouldn't check event type.
127   if (mInitializedByCtor) {
128     return mEvent->AsKeyboardEvent()->mCharCode;
129   }
130 
131   switch (mEvent->mMessage) {
132     case eKeyDown:
133     case eKeyDownOnPlugin:
134     case eKeyUp:
135     case eKeyUpOnPlugin:
136       return 0;
137     case eKeyPress:
138     case eAccessKeyNotFound:
139       return mEvent->AsKeyboardEvent()->mCharCode;
140     default:
141       break;
142   }
143   return 0;
144 }
145 
KeyCode(CallerType aCallerType)146 uint32_t KeyboardEvent::KeyCode(CallerType aCallerType) {
147   // If this event is initialized with ctor, we shouldn't check event type.
148   if (mInitializedByCtor) {
149     return mEvent->AsKeyboardEvent()->mKeyCode;
150   }
151 
152   if (!mEvent->HasKeyEventMessage()) {
153     return 0;
154   }
155 
156   if (!ShouldResistFingerprinting(aCallerType)) {
157     return mEvent->AsKeyboardEvent()->mKeyCode;
158   }
159 
160   // The keyCode should be zero if the char code is given.
161   if (CharCode()) {
162     return 0;
163   }
164 
165   // When fingerprinting resistance is enabled, we will give a spoofed keyCode
166   // according to the content-language of the document.
167   nsCOMPtr<nsIDocument> doc = GetDocument();
168   uint32_t spoofedKeyCode;
169 
170   if (nsRFPService::GetSpoofedKeyCode(doc, mEvent->AsKeyboardEvent(),
171                                       spoofedKeyCode)) {
172     return spoofedKeyCode;
173   }
174 
175   return 0;
176 }
177 
Which(CallerType aCallerType)178 uint32_t KeyboardEvent::Which(CallerType aCallerType) {
179   // If this event is initialized with ctor, which can have independent value.
180   if (mInitializedByCtor) {
181     return mInitializedWhichValue;
182   }
183 
184   switch (mEvent->mMessage) {
185     case eKeyDown:
186     case eKeyDownOnPlugin:
187     case eKeyUp:
188     case eKeyUpOnPlugin:
189       return KeyCode(aCallerType);
190     case eKeyPress:
191       // Special case for 4xp bug 62878.  Try to make value of which
192       // more closely mirror the values that 4.x gave for RETURN and BACKSPACE
193       {
194         uint32_t keyCode = mEvent->AsKeyboardEvent()->mKeyCode;
195         if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
196           return keyCode;
197         }
198         return CharCode();
199       }
200     default:
201       break;
202   }
203 
204   return 0;
205 }
206 
Location()207 uint32_t KeyboardEvent::Location() {
208   return mEvent->AsKeyboardEvent()->mLocation;
209 }
210 
211 // static
Constructor(const GlobalObject & aGlobal,const nsAString & aType,const KeyboardEventInit & aParam,ErrorResult & aRv)212 already_AddRefed<KeyboardEvent> KeyboardEvent::Constructor(
213     const GlobalObject& aGlobal, const nsAString& aType,
214     const KeyboardEventInit& aParam, ErrorResult& aRv) {
215   nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
216   RefPtr<KeyboardEvent> newEvent = new KeyboardEvent(target, nullptr, nullptr);
217   newEvent->InitWithKeyboardEventInit(target, aType, aParam, aRv);
218 
219   return newEvent.forget();
220 }
221 
InitWithKeyboardEventInit(EventTarget * aOwner,const nsAString & aType,const KeyboardEventInit & aParam,ErrorResult & aRv)222 void KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
223                                               const nsAString& aType,
224                                               const KeyboardEventInit& aParam,
225                                               ErrorResult& aRv) {
226   bool trusted = Init(aOwner);
227   InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, false,
228                false, false, false, aParam.mKeyCode, aParam.mCharCode);
229   InitModifiers(aParam);
230   SetTrusted(trusted);
231   mDetail = aParam.mDetail;
232   mInitializedByCtor = true;
233   mInitializedWhichValue = aParam.mWhich;
234 
235   WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
236   internalEvent->mLocation = aParam.mLocation;
237   internalEvent->mIsRepeat = aParam.mRepeat;
238   internalEvent->mIsComposing = aParam.mIsComposing;
239   internalEvent->mKeyNameIndex =
240       WidgetKeyboardEvent::GetKeyNameIndex(aParam.mKey);
241   if (internalEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
242     internalEvent->mKeyValue = aParam.mKey;
243   }
244   internalEvent->mCodeNameIndex =
245       WidgetKeyboardEvent::GetCodeNameIndex(aParam.mCode);
246   if (internalEvent->mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
247     internalEvent->mCodeValue = aParam.mCode;
248   }
249 }
250 
InitKeyEvent(const nsAString & aType,bool aCanBubble,bool aCancelable,nsGlobalWindowInner * aView,bool aCtrlKey,bool aAltKey,bool aShiftKey,bool aMetaKey,uint32_t aKeyCode,uint32_t aCharCode)251 void KeyboardEvent::InitKeyEvent(const nsAString& aType, bool aCanBubble,
252                                  bool aCancelable, nsGlobalWindowInner* aView,
253                                  bool aCtrlKey, bool aAltKey, bool aShiftKey,
254                                  bool aMetaKey, uint32_t aKeyCode,
255                                  uint32_t aCharCode) {
256   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
257 
258   UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
259 
260   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
261   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
262   keyEvent->mKeyCode = aKeyCode;
263   keyEvent->mCharCode = aCharCode;
264 }
265 
InitKeyboardEvent(const nsAString & aType,bool aCanBubble,bool aCancelable,nsGlobalWindowInner * aView,const nsAString & aKey,uint32_t aLocation,bool aCtrlKey,bool aAltKey,bool aShiftKey,bool aMetaKey,ErrorResult & aRv)266 void KeyboardEvent::InitKeyboardEvent(const nsAString& aType, bool aCanBubble,
267                                       bool aCancelable,
268                                       nsGlobalWindowInner* aView,
269                                       const nsAString& aKey, uint32_t aLocation,
270                                       bool aCtrlKey, bool aAltKey,
271                                       bool aShiftKey, bool aMetaKey,
272                                       ErrorResult& aRv) {
273   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
274 
275   UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
276 
277   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
278   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
279   keyEvent->mLocation = aLocation;
280   keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
281   keyEvent->mKeyValue = aKey;
282 }
283 
GetDocument()284 already_AddRefed<nsIDocument> KeyboardEvent::GetDocument() {
285   nsCOMPtr<nsIDocument> doc;
286   nsCOMPtr<EventTarget> eventTarget = InternalDOMEvent()->GetTarget();
287 
288   if (eventTarget) {
289     nsCOMPtr<nsPIDOMWindowInner> win =
290         do_QueryInterface(eventTarget->GetOwnerGlobal());
291 
292     if (win) {
293       doc = win->GetExtantDoc();
294     }
295   }
296 
297   return doc.forget();
298 }
299 
ShouldResistFingerprinting(CallerType aCallerType)300 bool KeyboardEvent::ShouldResistFingerprinting(CallerType aCallerType) {
301   // There are five situations we don't need to spoof this keyboard event.
302   //   1. This event is generated by scripts.
303   //   2. This event is from Numpad.
304   //   3. This event is in the system group.
305   //   4. The caller type is system.
306   //   5. The pref privcy.resistFingerprinting' is false, we fast return here
307   //      since we don't need to do any QI of following codes.
308   if (mInitializedByCtor || aCallerType == CallerType::System ||
309       mEvent->mFlags.mInSystemGroup ||
310       !nsContentUtils::ShouldResistFingerprinting() ||
311       mEvent->AsKeyboardEvent()->mLocation ==
312           KeyboardEventBinding::DOM_KEY_LOCATION_NUMPAD) {
313     return false;
314   }
315 
316   nsCOMPtr<nsIDocument> doc = GetDocument();
317 
318   return doc && !nsContentUtils::IsChromeDoc(doc);
319 }
320 
GetSpoofedModifierStates(const Modifiers aModifierKey,const bool aRawModifierState)321 bool KeyboardEvent::GetSpoofedModifierStates(const Modifiers aModifierKey,
322                                              const bool aRawModifierState) {
323   bool spoofedState;
324   nsCOMPtr<nsIDocument> doc = GetDocument();
325 
326   if (nsRFPService::GetSpoofedModifierStates(doc, mEvent->AsKeyboardEvent(),
327                                              aModifierKey, spoofedState)) {
328     return spoofedState;
329   }
330 
331   return aRawModifierState;
332 }
333 
334 }  // namespace dom
335 }  // namespace mozilla
336 
337 using namespace mozilla;
338 using namespace mozilla::dom;
339 
NS_NewDOMKeyboardEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetKeyboardEvent * aEvent)340 already_AddRefed<KeyboardEvent> NS_NewDOMKeyboardEvent(
341     EventTarget* aOwner, nsPresContext* aPresContext,
342     WidgetKeyboardEvent* aEvent) {
343   RefPtr<KeyboardEvent> it = new KeyboardEvent(aOwner, aPresContext, aEvent);
344   return it.forget();
345 }
346