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