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