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