Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / dom / events / KeyboardEvent.cpp
blob2f484d2129b420f3a058131a3182d1af33c44596
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/StaticPrefs_dom.h"
10 #include "mozilla/TextEvents.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsContentUtils.h"
13 #include "nsIPrincipal.h"
14 #include "nsRFPService.h"
15 #include "prtime.h"
17 namespace mozilla::dom {
19 KeyboardEvent::KeyboardEvent(EventTarget* aOwner, nsPresContext* aPresContext,
20 WidgetKeyboardEvent* aEvent)
21 : UIEvent(aOwner, aPresContext,
22 aEvent ? aEvent
23 : new WidgetKeyboardEvent(false, eVoidEvent, nullptr)),
24 mInitializedByJS(false),
25 mInitializedByCtor(false),
26 mInitializedWhichValue(0) {
27 if (aEvent) {
28 mEventIsInternal = false;
29 } else {
30 mEventIsInternal = true;
31 mEvent->mTime = PR_Now();
32 mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
36 bool KeyboardEvent::AltKey(CallerType aCallerType) {
37 bool altState = mEvent->AsKeyboardEvent()->IsAlt();
39 if (!ShouldResistFingerprinting(aCallerType)) {
40 return altState;
43 // We need to give a spoofed state for Alt key since it could be used as a
44 // modifier key in certain keyboard layout. For example, the '@' key for
45 // German keyboard for MAC is Alt+L.
46 return GetSpoofedModifierStates(Modifier::MODIFIER_ALT, altState);
49 bool KeyboardEvent::CtrlKey(CallerType aCallerType) {
50 // We don't spoof this key when privacy.resistFingerprinting
51 // is enabled, because it is often used for command key
52 // combinations in web apps.
53 return mEvent->AsKeyboardEvent()->IsControl();
56 bool KeyboardEvent::ShiftKey(CallerType aCallerType) {
57 bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
59 if (!ShouldResistFingerprinting(aCallerType)) {
60 return shiftState;
63 return GetSpoofedModifierStates(Modifier::MODIFIER_SHIFT, shiftState);
66 bool KeyboardEvent::MetaKey() {
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()->IsMeta();
73 bool KeyboardEvent::Repeat() { return mEvent->AsKeyboardEvent()->mIsRepeat; }
75 bool KeyboardEvent::IsComposing() {
76 return mEvent->AsKeyboardEvent()->mIsComposing;
79 void KeyboardEvent::GetKey(nsAString& aKeyName) const {
80 mEvent->AsKeyboardEvent()->GetDOMKeyName(aKeyName);
83 void KeyboardEvent::GetCode(nsAString& aCodeName, CallerType aCallerType) {
84 if (!ShouldResistFingerprinting(aCallerType)) {
85 mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
86 return;
89 // When fingerprinting resistance is enabled, we will give a spoofed code
90 // according to the content-language of the document.
91 nsCOMPtr<Document> doc = GetDocument();
93 nsRFPService::GetSpoofedCode(doc, mEvent->AsKeyboardEvent(), aCodeName);
96 void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam) {
97 GetKey(aParam.mKey);
98 GetCode(aParam.mCode);
99 aParam.mLocation = Location();
100 aParam.mRepeat = Repeat();
101 aParam.mIsComposing = IsComposing();
103 // legacy attributes
104 aParam.mKeyCode = KeyCode();
105 aParam.mCharCode = CharCode();
106 aParam.mWhich = Which();
108 // modifiers from EventModifierInit
109 aParam.mCtrlKey = CtrlKey();
110 aParam.mShiftKey = ShiftKey();
111 aParam.mAltKey = AltKey();
112 aParam.mMetaKey = MetaKey();
114 WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
115 aParam.mModifierAltGraph = internalEvent->IsAltGraph();
116 aParam.mModifierCapsLock = internalEvent->IsCapsLocked();
117 aParam.mModifierFn = internalEvent->IsFn();
118 aParam.mModifierFnLock = internalEvent->IsFnLocked();
119 aParam.mModifierNumLock = internalEvent->IsNumLocked();
120 aParam.mModifierOS = internalEvent->IsOS();
121 aParam.mModifierScrollLock = internalEvent->IsScrollLocked();
122 aParam.mModifierSymbol = internalEvent->IsSymbol();
123 aParam.mModifierSymbolLock = internalEvent->IsSymbolLocked();
125 // EventInit
126 aParam.mBubbles = internalEvent->mFlags.mBubbles;
127 aParam.mCancelable = internalEvent->mFlags.mCancelable;
130 bool KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
131 const WidgetKeyboardEvent& aWidgetKeyboardEvent,
132 CallerType aCallerType) const {
133 // - If this event is initialized by JS, we don't need to return same value
134 // for keyCode and charCode since they can be initialized separately.
135 // - If this is not a keypress event, we shouldn't return same value for
136 // keyCode and charCode.
137 // - If we need to return legacy keyCode and charCode values for the web
138 // app due to in the blacklist.
139 // - If this event is referred by default handler, i.e., the caller is
140 // system or this event is now in the system group, we don't need to use
141 // hack for web-compat.
142 if (mInitializedByJS || aWidgetKeyboardEvent.mMessage != eKeyPress ||
143 aWidgetKeyboardEvent.mUseLegacyKeyCodeAndCharCodeValues ||
144 aCallerType == CallerType::System ||
145 aWidgetKeyboardEvent.mFlags.mInSystemGroup) {
146 return false;
149 MOZ_ASSERT(aCallerType == CallerType::NonSystem);
151 return StaticPrefs::
152 dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value();
155 uint32_t KeyboardEvent::CharCode(CallerType aCallerType) {
156 WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
157 if (mInitializedByJS) {
158 // If this is initialized by Ctor, we should return the initialized value.
159 if (mInitializedByCtor) {
160 return widgetKeyboardEvent->mCharCode;
162 // Otherwise, i.e., initialized by InitKey*Event(), we should return the
163 // initialized value only when eKeyPress or eAccessKeyNotFound event.
164 // Although this is odd, but our traditional behavior.
165 return widgetKeyboardEvent->mMessage == eKeyPress ||
166 widgetKeyboardEvent->mMessage == eAccessKeyNotFound
167 ? widgetKeyboardEvent->mCharCode
168 : 0;
171 // If the key is a function key, we should return the result of KeyCode()
172 // even from CharCode(). Otherwise, i.e., the key may be a printable
173 // key or actually a printable key, we should return the given charCode
174 // value.
176 if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
177 ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
178 aCallerType)) {
179 return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
182 return widgetKeyboardEvent->mCharCode;
185 uint32_t KeyboardEvent::KeyCode(CallerType aCallerType) {
186 WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
187 if (mInitializedByJS) {
188 // If this is initialized by Ctor, we should return the initialized value.
189 if (mInitializedByCtor) {
190 return widgetKeyboardEvent->mKeyCode;
192 // Otherwise, i.e., initialized by InitKey*Event(), we should return the
193 // initialized value only when the event message is a valid keyboard event
194 // message. Although this is odd, but our traditional behavior.
195 // NOTE: The fix of bug 1222285 changed the behavior temporarily if
196 // spoofing is enabled. However, the behavior does not make sense
197 // since if the event is generated by JS, the behavior shouldn't
198 // be changed by whether spoofing is enabled or not. Therefore,
199 // we take back the original behavior.
200 return widgetKeyboardEvent->HasKeyEventMessage()
201 ? widgetKeyboardEvent->mKeyCode
202 : 0;
205 // If the key is not a function key, i.e., the key may be a printable key
206 // or a function key mapped as a printable key, we should use charCode value
207 // for keyCode value if this is a "keypress" event.
209 if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
210 ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
211 aCallerType)) {
212 return widgetKeyboardEvent->mCharCode;
215 return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
218 uint32_t KeyboardEvent::ComputeTraditionalKeyCode(
219 WidgetKeyboardEvent& aKeyboardEvent, CallerType aCallerType) {
220 if (!ShouldResistFingerprinting(aCallerType)) {
221 return aKeyboardEvent.mKeyCode;
224 // In Netscape style (i.e., traditional behavior of Gecko), the keyCode
225 // should be zero if the char code is given.
226 if ((mEvent->mMessage == eKeyPress ||
227 mEvent->mMessage == eAccessKeyNotFound) &&
228 aKeyboardEvent.mCharCode) {
229 return 0;
232 // When fingerprinting resistance is enabled, we will give a spoofed keyCode
233 // according to the content-language of the document.
234 nsCOMPtr<Document> doc = GetDocument();
235 uint32_t spoofedKeyCode;
237 if (nsRFPService::GetSpoofedKeyCode(doc, &aKeyboardEvent, spoofedKeyCode)) {
238 return spoofedKeyCode;
241 return 0;
244 uint32_t KeyboardEvent::Which(CallerType aCallerType) {
245 // If this event is initialized with ctor, which can have independent value.
246 if (mInitializedByCtor) {
247 return mInitializedWhichValue;
250 switch (mEvent->mMessage) {
251 case eKeyDown:
252 case eKeyUp:
253 return KeyCode(aCallerType);
254 case eKeyPress:
255 // Special case for 4xp bug 62878. Try to make value of which
256 // more closely mirror the values that 4.x gave for RETURN and BACKSPACE
258 uint32_t keyCode = mEvent->AsKeyboardEvent()->mKeyCode;
259 if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
260 return keyCode;
262 return CharCode();
264 default:
265 break;
268 return 0;
271 uint32_t KeyboardEvent::Location() {
272 return mEvent->AsKeyboardEvent()->mLocation;
275 // static
276 already_AddRefed<KeyboardEvent> KeyboardEvent::ConstructorJS(
277 const GlobalObject& aGlobal, const nsAString& aType,
278 const KeyboardEventInit& aParam) {
279 nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
280 RefPtr<KeyboardEvent> newEvent = new KeyboardEvent(target, nullptr, nullptr);
281 newEvent->InitWithKeyboardEventInit(target, aType, aParam);
283 return newEvent.forget();
286 void KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
287 const nsAString& aType,
288 const KeyboardEventInit& aParam) {
289 bool trusted = Init(aOwner);
290 InitKeyEventJS(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
291 false, false, false, false, aParam.mKeyCode, aParam.mCharCode);
292 InitModifiers(aParam);
293 SetTrusted(trusted);
294 mDetail = aParam.mDetail;
295 mInitializedByJS = true;
296 mInitializedByCtor = true;
297 mInitializedWhichValue = aParam.mWhich;
299 WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
300 internalEvent->mLocation = aParam.mLocation;
301 internalEvent->mIsRepeat = aParam.mRepeat;
302 internalEvent->mIsComposing = aParam.mIsComposing;
303 internalEvent->mKeyNameIndex =
304 WidgetKeyboardEvent::GetKeyNameIndex(aParam.mKey);
305 if (internalEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
306 internalEvent->mKeyValue = aParam.mKey;
308 internalEvent->mCodeNameIndex =
309 WidgetKeyboardEvent::GetCodeNameIndex(aParam.mCode);
310 if (internalEvent->mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
311 internalEvent->mCodeValue = aParam.mCode;
315 // static
316 bool KeyboardEvent::IsInitKeyEventAvailable(JSContext* aCx, JSObject*) {
317 if (StaticPrefs::dom_keyboardevent_init_key_event_enabled()) {
318 return true;
320 if (!StaticPrefs::dom_keyboardevent_init_key_event_enabled_in_addons()) {
321 return false;
323 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
324 return principal && principal->GetIsAddonOrExpandedAddonPrincipal();
327 void KeyboardEvent::InitKeyEventJS(const nsAString& aType, bool aCanBubble,
328 bool aCancelable, nsGlobalWindowInner* aView,
329 bool aCtrlKey, bool aAltKey, bool aShiftKey,
330 bool aMetaKey, uint32_t aKeyCode,
331 uint32_t aCharCode) {
332 NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
333 mInitializedByJS = true;
334 mInitializedByCtor = false;
336 UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
338 WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
339 keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
340 keyEvent->mKeyCode = aKeyCode;
341 keyEvent->mCharCode = aCharCode;
344 void KeyboardEvent::InitKeyboardEventJS(
345 const nsAString& aType, bool aCanBubble, bool aCancelable,
346 nsGlobalWindowInner* aView, const nsAString& aKey, uint32_t aLocation,
347 bool aCtrlKey, bool aAltKey, bool aShiftKey, bool aMetaKey) {
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->mLocation = aLocation;
357 keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
358 keyEvent->mKeyValue = aKey;
361 bool KeyboardEvent::ShouldResistFingerprinting(CallerType aCallerType) {
362 // There are five situations we don't need to spoof this keyboard event.
363 // 1. This event is initialized by scripts.
364 // 2. This event is from Numpad.
365 // 3. This event is in the system group.
366 // 4. The caller type is system.
367 // 5. The pref privcy.resistFingerprinting' is false, we fast return here
368 // since we don't need to do any QI of following codes.
369 if (mInitializedByJS || aCallerType == CallerType::System ||
370 mEvent->mFlags.mInSystemGroup ||
371 !nsContentUtils::ShouldResistFingerprinting() ||
372 mEvent->AsKeyboardEvent()->mLocation ==
373 KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
374 return false;
377 nsCOMPtr<Document> doc = GetDocument();
379 return nsContentUtils::ShouldResistFingerprinting(doc);
382 bool KeyboardEvent::GetSpoofedModifierStates(const Modifiers aModifierKey,
383 const bool aRawModifierState) {
384 bool spoofedState;
385 nsCOMPtr<Document> doc = GetDocument();
387 if (nsRFPService::GetSpoofedModifierStates(doc, mEvent->AsKeyboardEvent(),
388 aModifierKey, spoofedState)) {
389 return spoofedState;
392 return aRawModifierState;
395 } // namespace mozilla::dom
397 using namespace mozilla;
398 using namespace mozilla::dom;
400 already_AddRefed<KeyboardEvent> NS_NewDOMKeyboardEvent(
401 EventTarget* aOwner, nsPresContext* aPresContext,
402 WidgetKeyboardEvent* aEvent) {
403 RefPtr<KeyboardEvent> it = new KeyboardEvent(aOwner, aPresContext, aEvent);
404 return it.forget();