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/. */
7 #include "mozilla/dom/KeyboardEvent.h"
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/StaticPrefs_dom.h"
11 #include "mozilla/TextEvents.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/LookAndFeel.h"
14 #include "nsContentUtils.h"
15 #include "nsIPrincipal.h"
16 #include "nsRFPService.h"
19 namespace mozilla::dom
{
21 KeyboardEvent::KeyboardEvent(EventTarget
* aOwner
, nsPresContext
* aPresContext
,
22 WidgetKeyboardEvent
* aEvent
)
23 : UIEvent(aOwner
, aPresContext
,
25 : new WidgetKeyboardEvent(false, eVoidEvent
, nullptr)),
26 mInitializedByJS(false),
27 mInitializedByCtor(false),
28 mInitializedWhichValue(0) {
30 mEventIsInternal
= false;
32 mEventIsInternal
= true;
33 mEvent
->AsKeyboardEvent()->mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
37 bool KeyboardEvent::IsMenuAccessKeyPressed() const {
38 Modifiers mask
= LookAndFeel::GetMenuAccessKeyModifiers();
39 Modifiers modifiers
= GetModifiersForMenuAccessKey();
40 return mask
!= MODIFIER_SHIFT
&& (modifiers
& mask
) &&
41 (modifiers
& ~(mask
| MODIFIER_SHIFT
)) == 0;
44 static constexpr Modifiers kPossibleModifiersForAccessKey
=
45 MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
| MODIFIER_META
;
47 Modifiers
KeyboardEvent::GetModifiersForMenuAccessKey() const {
48 const WidgetInputEvent
* inputEvent
= WidgetEventPtr()->AsInputEvent();
49 MOZ_ASSERT(inputEvent
);
50 return inputEvent
->mModifiers
& kPossibleModifiersForAccessKey
;
53 bool KeyboardEvent::AltKey(CallerType aCallerType
) {
54 bool altState
= mEvent
->AsKeyboardEvent()->IsAlt();
56 if (!ShouldResistFingerprinting(aCallerType
)) {
60 // We need to give a spoofed state for Alt key since it could be used as a
61 // modifier key in certain keyboard layout. For example, the '@' key for
62 // German keyboard for MAC is Alt+L.
63 return GetSpoofedModifierStates(Modifier::MODIFIER_ALT
, altState
);
66 bool KeyboardEvent::CtrlKey(CallerType aCallerType
) {
67 // We don't spoof this key when privacy.resistFingerprinting
68 // is enabled, because it is often used for command key
69 // combinations in web apps.
70 return mEvent
->AsKeyboardEvent()->IsControl();
73 bool KeyboardEvent::ShiftKey(CallerType aCallerType
) {
74 bool shiftState
= mEvent
->AsKeyboardEvent()->IsShift();
76 if (!ShouldResistFingerprinting(aCallerType
)) {
80 return GetSpoofedModifierStates(Modifier::MODIFIER_SHIFT
, shiftState
);
83 bool KeyboardEvent::MetaKey() {
84 // We don't spoof this key when privacy.resistFingerprinting
85 // is enabled, because it is often used for command key
86 // combinations in web apps.
87 return mEvent
->AsKeyboardEvent()->IsMeta();
90 bool KeyboardEvent::Repeat() { return mEvent
->AsKeyboardEvent()->mIsRepeat
; }
92 bool KeyboardEvent::IsComposing() {
93 return mEvent
->AsKeyboardEvent()->mIsComposing
;
96 void KeyboardEvent::GetKey(nsAString
& aKeyName
) const {
97 mEvent
->AsKeyboardEvent()->GetDOMKeyName(aKeyName
);
100 void KeyboardEvent::GetCode(nsAString
& aCodeName
, CallerType aCallerType
) {
101 if (!ShouldResistFingerprinting(aCallerType
)) {
102 mEvent
->AsKeyboardEvent()->GetDOMCodeName(aCodeName
);
106 // When fingerprinting resistance is enabled, we will give a spoofed code
107 // according to the content-language of the document.
108 nsCOMPtr
<Document
> doc
= GetDocument();
110 nsRFPService::GetSpoofedCode(doc
, mEvent
->AsKeyboardEvent(), aCodeName
);
113 void KeyboardEvent::GetInitDict(KeyboardEventInit
& aParam
) {
115 GetCode(aParam
.mCode
);
116 aParam
.mLocation
= Location();
117 aParam
.mRepeat
= Repeat();
118 aParam
.mIsComposing
= IsComposing();
121 aParam
.mKeyCode
= KeyCode();
122 aParam
.mCharCode
= CharCode();
123 aParam
.mWhich
= Which();
125 // modifiers from EventModifierInit
126 aParam
.mCtrlKey
= CtrlKey();
127 aParam
.mShiftKey
= ShiftKey();
128 aParam
.mAltKey
= AltKey();
129 aParam
.mMetaKey
= MetaKey();
131 WidgetKeyboardEvent
* internalEvent
= mEvent
->AsKeyboardEvent();
132 aParam
.mModifierAltGraph
= internalEvent
->IsAltGraph();
133 aParam
.mModifierCapsLock
= internalEvent
->IsCapsLocked();
134 aParam
.mModifierFn
= internalEvent
->IsFn();
135 aParam
.mModifierFnLock
= internalEvent
->IsFnLocked();
136 aParam
.mModifierNumLock
= internalEvent
->IsNumLocked();
137 aParam
.mModifierScrollLock
= internalEvent
->IsScrollLocked();
138 aParam
.mModifierSymbol
= internalEvent
->IsSymbol();
139 aParam
.mModifierSymbolLock
= internalEvent
->IsSymbolLocked();
142 aParam
.mBubbles
= internalEvent
->mFlags
.mBubbles
;
143 aParam
.mCancelable
= internalEvent
->mFlags
.mCancelable
;
146 bool KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
147 const WidgetKeyboardEvent
& aWidgetKeyboardEvent
,
148 CallerType aCallerType
) const {
149 // - If this event is initialized by JS, we don't need to return same value
150 // for keyCode and charCode since they can be initialized separately.
151 // - If this is not a keypress event, we shouldn't return same value for
152 // keyCode and charCode.
153 // - If we need to return legacy keyCode and charCode values for the web
154 // app due to in the blacklist.
155 // - If this event is referred by default handler, i.e., the caller is
156 // system or this event is now in the system group, we don't need to use
157 // hack for web-compat.
158 if (mInitializedByJS
|| aWidgetKeyboardEvent
.mMessage
!= eKeyPress
||
159 aWidgetKeyboardEvent
.mUseLegacyKeyCodeAndCharCodeValues
||
160 aCallerType
== CallerType::System
||
161 aWidgetKeyboardEvent
.mFlags
.mInSystemGroup
) {
165 MOZ_ASSERT(aCallerType
== CallerType::NonSystem
);
168 dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value();
171 uint32_t KeyboardEvent::CharCode(CallerType aCallerType
) {
172 WidgetKeyboardEvent
* widgetKeyboardEvent
= mEvent
->AsKeyboardEvent();
173 if (mInitializedByJS
) {
174 // If this is initialized by Ctor, we should return the initialized value.
175 if (mInitializedByCtor
) {
176 return widgetKeyboardEvent
->mCharCode
;
178 // Otherwise, i.e., initialized by InitKey*Event(), we should return the
179 // initialized value only when eKeyPress or eAccessKeyNotFound event.
180 // Although this is odd, but our traditional behavior.
181 return widgetKeyboardEvent
->mMessage
== eKeyPress
||
182 widgetKeyboardEvent
->mMessage
== eAccessKeyNotFound
183 ? widgetKeyboardEvent
->mCharCode
187 // If the key is a function key, we should return the result of KeyCode()
188 // even from CharCode(). Otherwise, i.e., the key may be a printable
189 // key or actually a printable key, we should return the given charCode
192 if (widgetKeyboardEvent
->mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
&&
193 ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent
,
195 return ComputeTraditionalKeyCode(*widgetKeyboardEvent
, aCallerType
);
198 return widgetKeyboardEvent
->mCharCode
;
201 uint32_t KeyboardEvent::KeyCode(CallerType aCallerType
) {
202 WidgetKeyboardEvent
* widgetKeyboardEvent
= mEvent
->AsKeyboardEvent();
203 if (mInitializedByJS
) {
204 // If this is initialized by Ctor, we should return the initialized value.
205 if (mInitializedByCtor
) {
206 return widgetKeyboardEvent
->mKeyCode
;
208 // Otherwise, i.e., initialized by InitKey*Event(), we should return the
209 // initialized value only when the event message is a valid keyboard event
210 // message. Although this is odd, but our traditional behavior.
211 // NOTE: The fix of bug 1222285 changed the behavior temporarily if
212 // spoofing is enabled. However, the behavior does not make sense
213 // since if the event is generated by JS, the behavior shouldn't
214 // be changed by whether spoofing is enabled or not. Therefore,
215 // we take back the original behavior.
216 return widgetKeyboardEvent
->HasKeyEventMessage()
217 ? widgetKeyboardEvent
->mKeyCode
221 // If the key is not a function key, i.e., the key may be a printable key
222 // or a function key mapped as a printable key, we should use charCode value
223 // for keyCode value if this is a "keypress" event.
225 if (widgetKeyboardEvent
->mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
&&
226 ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent
,
228 return widgetKeyboardEvent
->mCharCode
;
231 return ComputeTraditionalKeyCode(*widgetKeyboardEvent
, aCallerType
);
234 uint32_t KeyboardEvent::ComputeTraditionalKeyCode(
235 WidgetKeyboardEvent
& aKeyboardEvent
, CallerType aCallerType
) {
236 if (!ShouldResistFingerprinting(aCallerType
)) {
237 return aKeyboardEvent
.mKeyCode
;
240 // In Netscape style (i.e., traditional behavior of Gecko), the keyCode
241 // should be zero if the char code is given.
242 if ((mEvent
->mMessage
== eKeyPress
||
243 mEvent
->mMessage
== eAccessKeyNotFound
) &&
244 aKeyboardEvent
.mCharCode
) {
248 // When fingerprinting resistance is enabled, we will give a spoofed keyCode
249 // according to the content-language of the document.
250 nsCOMPtr
<Document
> doc
= GetDocument();
251 uint32_t spoofedKeyCode
;
253 if (nsRFPService::GetSpoofedKeyCode(doc
, &aKeyboardEvent
, spoofedKeyCode
)) {
254 return spoofedKeyCode
;
260 uint32_t KeyboardEvent::Which(CallerType aCallerType
) {
261 // If this event is initialized with ctor, which can have independent value.
262 if (mInitializedByCtor
) {
263 return mInitializedWhichValue
;
266 switch (mEvent
->mMessage
) {
269 return KeyCode(aCallerType
);
271 // Special case for 4xp bug 62878. Try to make value of which
272 // more closely mirror the values that 4.x gave for RETURN and BACKSPACE
274 uint32_t keyCode
= mEvent
->AsKeyboardEvent()->mKeyCode
;
275 if (keyCode
== NS_VK_RETURN
|| keyCode
== NS_VK_BACK
) {
287 uint32_t KeyboardEvent::Location() {
288 return mEvent
->AsKeyboardEvent()->mLocation
;
292 already_AddRefed
<KeyboardEvent
> KeyboardEvent::ConstructorJS(
293 const GlobalObject
& aGlobal
, const nsAString
& aType
,
294 const KeyboardEventInit
& aParam
) {
295 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(aGlobal
.GetAsSupports());
296 RefPtr
<KeyboardEvent
> newEvent
= new KeyboardEvent(target
, nullptr, nullptr);
297 newEvent
->InitWithKeyboardEventInit(target
, aType
, aParam
);
299 return newEvent
.forget();
302 void KeyboardEvent::InitWithKeyboardEventInit(EventTarget
* aOwner
,
303 const nsAString
& aType
,
304 const KeyboardEventInit
& aParam
) {
305 bool trusted
= Init(aOwner
);
306 InitKeyEventJS(aType
, aParam
.mBubbles
, aParam
.mCancelable
, aParam
.mView
,
307 false, false, false, false, aParam
.mKeyCode
, aParam
.mCharCode
);
308 InitModifiers(aParam
);
310 mDetail
= aParam
.mDetail
;
311 mInitializedByJS
= true;
312 mInitializedByCtor
= true;
313 mInitializedWhichValue
= aParam
.mWhich
;
315 WidgetKeyboardEvent
* internalEvent
= mEvent
->AsKeyboardEvent();
316 internalEvent
->mLocation
= aParam
.mLocation
;
317 internalEvent
->mIsRepeat
= aParam
.mRepeat
;
318 internalEvent
->mIsComposing
= aParam
.mIsComposing
;
319 internalEvent
->mKeyNameIndex
=
320 WidgetKeyboardEvent::GetKeyNameIndex(aParam
.mKey
);
321 if (internalEvent
->mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
322 internalEvent
->mKeyValue
= aParam
.mKey
;
324 internalEvent
->mCodeNameIndex
=
325 WidgetKeyboardEvent::GetCodeNameIndex(aParam
.mCode
);
326 if (internalEvent
->mCodeNameIndex
== CODE_NAME_INDEX_USE_STRING
) {
327 internalEvent
->mCodeValue
= aParam
.mCode
;
332 bool KeyboardEvent::IsInitKeyEventAvailable(JSContext
* aCx
, JSObject
*) {
333 if (StaticPrefs::dom_keyboardevent_init_key_event_enabled()) {
336 if (!StaticPrefs::dom_keyboardevent_init_key_event_enabled_in_addons()) {
339 nsIPrincipal
* principal
= nsContentUtils::SubjectPrincipal(aCx
);
340 return principal
&& principal
->GetIsAddonOrExpandedAddonPrincipal();
343 void KeyboardEvent::InitKeyEventJS(const nsAString
& aType
, bool aCanBubble
,
344 bool aCancelable
, nsGlobalWindowInner
* aView
,
345 bool aCtrlKey
, bool aAltKey
, bool aShiftKey
,
346 bool aMetaKey
, uint32_t aKeyCode
,
347 uint32_t aCharCode
) {
348 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
349 mInitializedByJS
= true;
350 mInitializedByCtor
= false;
352 UIEvent::InitUIEvent(aType
, aCanBubble
, aCancelable
, aView
, 0);
354 WidgetKeyboardEvent
* keyEvent
= mEvent
->AsKeyboardEvent();
355 keyEvent
->InitBasicModifiers(aCtrlKey
, aAltKey
, aShiftKey
, aMetaKey
);
356 keyEvent
->mKeyCode
= aKeyCode
;
357 keyEvent
->mCharCode
= aCharCode
;
360 void KeyboardEvent::InitKeyboardEventJS(
361 const nsAString
& aType
, bool aCanBubble
, bool aCancelable
,
362 nsGlobalWindowInner
* aView
, const nsAString
& aKey
, uint32_t aLocation
,
363 bool aCtrlKey
, bool aAltKey
, bool aShiftKey
, bool aMetaKey
) {
364 NS_ENSURE_TRUE_VOID(!mEvent
->mFlags
.mIsBeingDispatched
);
365 mInitializedByJS
= true;
366 mInitializedByCtor
= false;
368 UIEvent::InitUIEvent(aType
, aCanBubble
, aCancelable
, aView
, 0);
370 WidgetKeyboardEvent
* keyEvent
= mEvent
->AsKeyboardEvent();
371 keyEvent
->InitBasicModifiers(aCtrlKey
, aAltKey
, aShiftKey
, aMetaKey
);
372 keyEvent
->mLocation
= aLocation
;
373 keyEvent
->mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
374 keyEvent
->mKeyValue
= aKey
;
377 bool KeyboardEvent::ShouldResistFingerprinting(CallerType aCallerType
) {
378 // There are five situations we don't need to spoof this keyboard event.
379 // 1. The pref privcy.resistFingerprinting' is false, we fast return here.
380 // 2. This event is initialized by scripts.
381 // 3. This event is from Numpad.
382 // 4. This event is in the system group.
383 // 5. The caller type is system.
384 if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
385 RFPTarget::KeyboardEvents
) ||
386 mInitializedByJS
|| aCallerType
== CallerType::System
||
387 mEvent
->mFlags
.mInSystemGroup
||
388 mEvent
->AsKeyboardEvent()->mLocation
==
389 KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD
) {
393 nsCOMPtr
<Document
> doc
= GetDocument();
394 // We've checked the pref above, so use true as fallback if doc is null.
395 return doc
? doc
->ShouldResistFingerprinting(RFPTarget::KeyboardEvents
)
399 bool KeyboardEvent::GetSpoofedModifierStates(const Modifiers aModifierKey
,
400 const bool aRawModifierState
) {
402 nsCOMPtr
<Document
> doc
= GetDocument();
404 if (nsRFPService::GetSpoofedModifierStates(doc
, mEvent
->AsKeyboardEvent(),
405 aModifierKey
, spoofedState
)) {
409 return aRawModifierState
;
412 } // namespace mozilla::dom
414 using namespace mozilla
;
415 using namespace mozilla::dom
;
417 already_AddRefed
<KeyboardEvent
> NS_NewDOMKeyboardEvent(
418 EventTarget
* aOwner
, nsPresContext
* aPresContext
,
419 WidgetKeyboardEvent
* aEvent
) {
420 RefPtr
<KeyboardEvent
> it
= new KeyboardEvent(aOwner
, aPresContext
, aEvent
);