1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "gfxPrefs.h"
7 #include "mozilla/BasicEvents.h"
8 #include "mozilla/ContentEvents.h"
9 #include "mozilla/InternalMutationEvent.h"
10 #include "mozilla/MiscEvents.h"
11 #include "mozilla/MouseEvents.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/TextEvents.h"
14 #include "mozilla/TouchEvents.h"
15 #include "nsPrintfCString.h"
16 
17 namespace mozilla {
18 
19 /******************************************************************************
20  * Global helper methods
21  ******************************************************************************/
22 
23 const char*
ToChar(EventMessage aEventMessage)24 ToChar(EventMessage aEventMessage)
25 {
26   switch (aEventMessage) {
27 
28 #define NS_EVENT_MESSAGE(aMessage) \
29     case aMessage: \
30       return #aMessage;
31 
32 #include "mozilla/EventMessageList.h"
33 
34 #undef NS_EVENT_MESSAGE
35     default:
36       return "illegal event message";
37   }
38 }
39 
40 const char*
ToChar(EventClassID aEventClassID)41 ToChar(EventClassID aEventClassID)
42 {
43   switch (aEventClassID) {
44 
45 #define NS_ROOT_EVENT_CLASS(aPrefix, aName) \
46     case eBasic##aName##Class: \
47       return "eBasic" #aName "Class";
48 
49 #define NS_EVENT_CLASS(aPrefix, aName) \
50     case e##aName##Class: \
51       return "e" #aName "Class";
52 
53 #include "mozilla/EventClassList.h"
54 
55 #undef NS_EVENT_CLASS
56 #undef NS_ROOT_EVENT_CLASS
57     default:
58       return "illegal event class ID";
59   }
60 }
61 
62 const nsCString
ToString(KeyNameIndex aKeyNameIndex)63 ToString(KeyNameIndex aKeyNameIndex)
64 {
65   if (aKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
66     return NS_LITERAL_CSTRING("USE_STRING");
67   }
68   nsAutoString keyName;
69   WidgetKeyboardEvent::GetDOMKeyName(aKeyNameIndex, keyName);
70   return NS_ConvertUTF16toUTF8(keyName);
71 }
72 
73 const nsCString
ToString(CodeNameIndex aCodeNameIndex)74 ToString(CodeNameIndex aCodeNameIndex)
75 {
76   if (aCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
77     return NS_LITERAL_CSTRING("USE_STRING");
78   }
79   nsAutoString codeName;
80   WidgetKeyboardEvent::GetDOMCodeName(aCodeNameIndex, codeName);
81   return NS_ConvertUTF16toUTF8(codeName);
82 }
83 
84 const nsCString
GetDOMKeyCodeName(uint32_t aKeyCode)85 GetDOMKeyCodeName(uint32_t aKeyCode)
86 {
87   switch (aKeyCode) {
88 #define NS_DISALLOW_SAME_KEYCODE
89 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
90     case aDOMKeyCode: \
91       return NS_LITERAL_CSTRING(#aDOMKeyName);
92 
93 #include "mozilla/VirtualKeyCodeList.h"
94 
95 #undef NS_DEFINE_VK
96 #undef NS_DISALLOW_SAME_KEYCODE
97 
98     default:
99       return nsPrintfCString("Invalid DOM keyCode (0x%08X)", aKeyCode);
100   }
101 }
102 
103 bool
IsValidRawTextRangeValue(RawTextRangeType aRawTextRangeType)104 IsValidRawTextRangeValue(RawTextRangeType aRawTextRangeType)
105 {
106   switch (static_cast<TextRangeType>(aRawTextRangeType)) {
107     case TextRangeType::eUninitialized:
108     case TextRangeType::eCaret:
109     case TextRangeType::eRawClause:
110     case TextRangeType::eSelectedRawClause:
111     case TextRangeType::eConvertedClause:
112     case TextRangeType::eSelectedClause:
113       return true;
114     default:
115       return false;
116   }
117 }
118 
119 RawTextRangeType
ToRawTextRangeType(TextRangeType aTextRangeType)120 ToRawTextRangeType(TextRangeType aTextRangeType)
121 {
122   return static_cast<RawTextRangeType>(aTextRangeType);
123 }
124 
125 TextRangeType
ToTextRangeType(RawTextRangeType aRawTextRangeType)126 ToTextRangeType(RawTextRangeType aRawTextRangeType)
127 {
128   MOZ_ASSERT(IsValidRawTextRangeValue(aRawTextRangeType));
129   return static_cast<TextRangeType>(aRawTextRangeType);
130 }
131 
132 const char*
ToChar(TextRangeType aTextRangeType)133 ToChar(TextRangeType aTextRangeType)
134 {
135   switch (aTextRangeType) {
136     case TextRangeType::eUninitialized:
137       return "TextRangeType::eUninitialized";
138     case TextRangeType::eCaret:
139       return "TextRangeType::eCaret";
140     case TextRangeType::eRawClause:
141       return "TextRangeType::eRawClause";
142     case TextRangeType::eSelectedRawClause:
143       return "TextRangeType::eSelectedRawClause";
144     case TextRangeType::eConvertedClause:
145       return "TextRangeType::eConvertedClause";
146     case TextRangeType::eSelectedClause:
147       return "TextRangeType::eSelectedClause";
148     default:
149       return "Invalid TextRangeType";
150   }
151 }
152 
153 SelectionType
ToSelectionType(TextRangeType aTextRangeType)154 ToSelectionType(TextRangeType aTextRangeType)
155 {
156   switch (aTextRangeType) {
157     case TextRangeType::eRawClause:
158       return SelectionType::eIMERawClause;
159     case TextRangeType::eSelectedRawClause:
160       return SelectionType::eIMESelectedRawClause;
161     case TextRangeType::eConvertedClause:
162       return SelectionType::eIMEConvertedClause;
163     case TextRangeType::eSelectedClause:
164       return SelectionType::eIMESelectedClause;
165     default:
166       MOZ_CRASH("TextRangeType is invalid");
167       return SelectionType::eNormal;
168   }
169 }
170 
171 /******************************************************************************
172  * As*Event() implementation
173  ******************************************************************************/
174 
175 #define NS_ROOT_EVENT_CLASS(aPrefix, aName)
176 #define NS_EVENT_CLASS(aPrefix, aName) \
177 aPrefix##aName* \
178 WidgetEvent::As##aName() \
179 { \
180   return nullptr; \
181 } \
182 \
183 const aPrefix##aName* \
184 WidgetEvent::As##aName() const \
185 { \
186   return const_cast<WidgetEvent*>(this)->As##aName(); \
187 }
188 
189 #include "mozilla/EventClassList.h"
190 
191 #undef NS_EVENT_CLASS
192 #undef NS_ROOT_EVENT_CLASS
193 
194 /******************************************************************************
195  * mozilla::WidgetEvent
196  *
197  * Event struct type checking methods.
198  ******************************************************************************/
199 
200 bool
IsQueryContentEvent() const201 WidgetEvent::IsQueryContentEvent() const
202 {
203   return mClass == eQueryContentEventClass;
204 }
205 
206 bool
IsSelectionEvent() const207 WidgetEvent::IsSelectionEvent() const
208 {
209   return mClass == eSelectionEventClass;
210 }
211 
212 bool
IsContentCommandEvent() const213 WidgetEvent::IsContentCommandEvent() const
214 {
215   return mClass == eContentCommandEventClass;
216 }
217 
218 bool
IsNativeEventDelivererForPlugin() const219 WidgetEvent::IsNativeEventDelivererForPlugin() const
220 {
221   return mClass == ePluginEventClass;
222 }
223 
224 
225 /******************************************************************************
226  * mozilla::WidgetEvent
227  *
228  * Event message checking methods.
229  ******************************************************************************/
230 
231 bool
HasMouseEventMessage() const232 WidgetEvent::HasMouseEventMessage() const
233 {
234   switch (mMessage) {
235     case eMouseDown:
236     case eMouseUp:
237     case eMouseClick:
238     case eMouseDoubleClick:
239     case eMouseEnterIntoWidget:
240     case eMouseExitFromWidget:
241     case eMouseActivate:
242     case eMouseOver:
243     case eMouseOut:
244     case eMouseHitTest:
245     case eMouseMove:
246       return true;
247     default:
248       return false;
249   }
250 }
251 
252 bool
HasDragEventMessage() const253 WidgetEvent::HasDragEventMessage() const
254 {
255   switch (mMessage) {
256     case eDragEnter:
257     case eDragOver:
258     case eDragExit:
259     case eDrag:
260     case eDragEnd:
261     case eDragStart:
262     case eDrop:
263     case eDragLeave:
264       return true;
265     default:
266       return false;
267   }
268 }
269 
270 bool
HasKeyEventMessage() const271 WidgetEvent::HasKeyEventMessage() const
272 {
273   switch (mMessage) {
274     case eKeyDown:
275     case eKeyPress:
276     case eKeyUp:
277     case eKeyDownOnPlugin:
278     case eKeyUpOnPlugin:
279     case eBeforeKeyDown:
280     case eBeforeKeyUp:
281     case eAfterKeyDown:
282     case eAfterKeyUp:
283     case eAccessKeyNotFound:
284       return true;
285     default:
286       return false;
287   }
288 }
289 
290 bool
HasIMEEventMessage() const291 WidgetEvent::HasIMEEventMessage() const
292 {
293   switch (mMessage) {
294     case eCompositionStart:
295     case eCompositionEnd:
296     case eCompositionUpdate:
297     case eCompositionChange:
298     case eCompositionCommitAsIs:
299     case eCompositionCommit:
300       return true;
301     default:
302       return false;
303   }
304 }
305 
306 bool
HasPluginActivationEventMessage() const307 WidgetEvent::HasPluginActivationEventMessage() const
308 {
309   return mMessage == ePluginActivate ||
310          mMessage == ePluginFocus;
311 }
312 
313 /******************************************************************************
314  * mozilla::WidgetEvent
315  *
316  * Specific event checking methods.
317  ******************************************************************************/
318 
319 bool
IsRetargetedNativeEventDelivererForPlugin() const320 WidgetEvent::IsRetargetedNativeEventDelivererForPlugin() const
321 {
322   const WidgetPluginEvent* pluginEvent = AsPluginEvent();
323   return pluginEvent && pluginEvent->mRetargetToFocusedDocument;
324 }
325 
326 bool
IsNonRetargetedNativeEventDelivererForPlugin() const327 WidgetEvent::IsNonRetargetedNativeEventDelivererForPlugin() const
328 {
329   const WidgetPluginEvent* pluginEvent = AsPluginEvent();
330   return pluginEvent && !pluginEvent->mRetargetToFocusedDocument;
331 }
332 
333 bool
IsIMERelatedEvent() const334 WidgetEvent::IsIMERelatedEvent() const
335 {
336   return HasIMEEventMessage() || IsQueryContentEvent() || IsSelectionEvent();
337 }
338 
339 bool
IsUsingCoordinates() const340 WidgetEvent::IsUsingCoordinates() const
341 {
342   const WidgetMouseEvent* mouseEvent = AsMouseEvent();
343   if (mouseEvent) {
344     return !mouseEvent->IsContextMenuKeyEvent();
345   }
346   return !HasKeyEventMessage() && !IsIMERelatedEvent() &&
347          !HasPluginActivationEventMessage() &&
348          !IsNativeEventDelivererForPlugin() &&
349          !IsContentCommandEvent();
350 }
351 
352 bool
IsTargetedAtFocusedWindow() const353 WidgetEvent::IsTargetedAtFocusedWindow() const
354 {
355   const WidgetMouseEvent* mouseEvent = AsMouseEvent();
356   if (mouseEvent) {
357     return mouseEvent->IsContextMenuKeyEvent();
358   }
359   return HasKeyEventMessage() || IsIMERelatedEvent() ||
360          IsContentCommandEvent() ||
361          IsRetargetedNativeEventDelivererForPlugin();
362 }
363 
364 bool
IsTargetedAtFocusedContent() const365 WidgetEvent::IsTargetedAtFocusedContent() const
366 {
367   const WidgetMouseEvent* mouseEvent = AsMouseEvent();
368   if (mouseEvent) {
369     return mouseEvent->IsContextMenuKeyEvent();
370   }
371   return HasKeyEventMessage() || IsIMERelatedEvent() ||
372          IsRetargetedNativeEventDelivererForPlugin();
373 }
374 
375 bool
IsAllowedToDispatchDOMEvent() const376 WidgetEvent::IsAllowedToDispatchDOMEvent() const
377 {
378   switch (mClass) {
379     case eMouseEventClass:
380       if (mMessage == eMouseTouchDrag) {
381         return false;
382       }
383       MOZ_FALLTHROUGH;
384     case ePointerEventClass:
385       // We want synthesized mouse moves to cause mouseover and mouseout
386       // DOM events (EventStateManager::PreHandleEvent), but not mousemove
387       // DOM events.
388       // Synthesized button up events also do not cause DOM events because they
389       // do not have a reliable mRefPoint.
390       return AsMouseEvent()->mReason == WidgetMouseEvent::eReal;
391 
392     case eWheelEventClass: {
393       // wheel event whose all delta values are zero by user pref applied, it
394       // shouldn't cause a DOM event.
395       const WidgetWheelEvent* wheelEvent = AsWheelEvent();
396       return wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0 ||
397              wheelEvent->mDeltaZ != 0.0;
398     }
399 
400     // Following events are handled in EventStateManager, so, we don't need to
401     // dispatch DOM event for them into the DOM tree.
402     case eQueryContentEventClass:
403     case eSelectionEventClass:
404     case eContentCommandEventClass:
405       return false;
406 
407     default:
408       return true;
409   }
410 }
411 
412 /******************************************************************************
413  * mozilla::WidgetInputEvent
414  ******************************************************************************/
415 
416 /* static */
417 Modifier
GetModifier(const nsAString & aDOMKeyName)418 WidgetInputEvent::GetModifier(const nsAString& aDOMKeyName)
419 {
420   if (aDOMKeyName.EqualsLiteral("Accel")) {
421     return AccelModifier();
422   }
423   KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aDOMKeyName);
424   return WidgetKeyboardEvent::GetModifierForKeyName(keyNameIndex);
425 }
426 
427 /* static */
428 Modifier
AccelModifier()429 WidgetInputEvent::AccelModifier()
430 {
431   static Modifier sAccelModifier = MODIFIER_NONE;
432   if (sAccelModifier == MODIFIER_NONE) {
433     switch (Preferences::GetInt("ui.key.accelKey", 0)) {
434       case nsIDOMKeyEvent::DOM_VK_META:
435         sAccelModifier = MODIFIER_META;
436         break;
437       case nsIDOMKeyEvent::DOM_VK_WIN:
438         sAccelModifier = MODIFIER_OS;
439         break;
440       case nsIDOMKeyEvent::DOM_VK_ALT:
441         sAccelModifier = MODIFIER_ALT;
442         break;
443       case nsIDOMKeyEvent::DOM_VK_CONTROL:
444         sAccelModifier = MODIFIER_CONTROL;
445         break;
446       default:
447 #ifdef XP_MACOSX
448         sAccelModifier = MODIFIER_META;
449 #else
450         sAccelModifier = MODIFIER_CONTROL;
451 #endif
452         break;
453     }
454   }
455   return sAccelModifier;
456 }
457 
458 /******************************************************************************
459  * mozilla::WidgetWheelEvent (MouseEvents.h)
460  ******************************************************************************/
461 
462 /* static */ double
ComputeOverriddenDelta(double aDelta,bool aIsForVertical)463 WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, bool aIsForVertical)
464 {
465   if (!gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) {
466     return aDelta;
467   }
468   int32_t intFactor = aIsForVertical
469                       ? gfxPrefs::MouseWheelRootScrollVerticalFactor()
470                       : gfxPrefs::MouseWheelRootScrollHorizontalFactor();
471   // Making the scroll speed slower doesn't make sense. So, ignore odd factor
472   // which is less than 1.0.
473   if (intFactor <= 100) {
474     return aDelta;
475   }
476   double factor = static_cast<double>(intFactor) / 100;
477   return aDelta * factor;
478 }
479 
480 double
OverriddenDeltaX() const481 WidgetWheelEvent::OverriddenDeltaX() const
482 {
483   if (!mAllowToOverrideSystemScrollSpeed) {
484     return mDeltaX;
485   }
486   return ComputeOverriddenDelta(mDeltaX, false);
487 }
488 
489 double
OverriddenDeltaY() const490 WidgetWheelEvent::OverriddenDeltaY() const
491 {
492   if (!mAllowToOverrideSystemScrollSpeed) {
493     return mDeltaY;
494   }
495   return ComputeOverriddenDelta(mDeltaY, true);
496 }
497 
498 /******************************************************************************
499  * mozilla::WidgetKeyboardEvent (TextEvents.h)
500  ******************************************************************************/
501 
502 #define NS_DEFINE_KEYNAME(aCPPName, aDOMKeyName) (u"" aDOMKeyName),
503 const char16_t* const WidgetKeyboardEvent::kKeyNames[] = {
504 #include "mozilla/KeyNameList.h"
505 };
506 #undef NS_DEFINE_KEYNAME
507 
508 #define NS_DEFINE_PHYSICAL_KEY_CODE_NAME(aCPPName, aDOMCodeName) \
509     (u"" aDOMCodeName),
510 const char16_t* const WidgetKeyboardEvent::kCodeNames[] = {
511 #include "mozilla/PhysicalKeyCodeNameList.h"
512 };
513 #undef NS_DEFINE_PHYSICAL_KEY_CODE_NAME
514 
515 WidgetKeyboardEvent::KeyNameIndexHashtable*
516   WidgetKeyboardEvent::sKeyNameIndexHashtable = nullptr;
517 WidgetKeyboardEvent::CodeNameIndexHashtable*
518   WidgetKeyboardEvent::sCodeNameIndexHashtable = nullptr;
519 
520 bool
ShouldCauseKeypressEvents() const521 WidgetKeyboardEvent::ShouldCauseKeypressEvents() const
522 {
523   // Currently, we don't dispatch keypress events of modifier keys and
524   // dead keys.
525   switch (mKeyNameIndex) {
526     case KEY_NAME_INDEX_Alt:
527     case KEY_NAME_INDEX_AltGraph:
528     case KEY_NAME_INDEX_CapsLock:
529     case KEY_NAME_INDEX_Control:
530     case KEY_NAME_INDEX_Fn:
531     case KEY_NAME_INDEX_FnLock:
532     // case KEY_NAME_INDEX_Hyper:
533     case KEY_NAME_INDEX_Meta:
534     case KEY_NAME_INDEX_NumLock:
535     case KEY_NAME_INDEX_OS:
536     case KEY_NAME_INDEX_ScrollLock:
537     case KEY_NAME_INDEX_Shift:
538     // case KEY_NAME_INDEX_Super:
539     case KEY_NAME_INDEX_Symbol:
540     case KEY_NAME_INDEX_SymbolLock:
541     case KEY_NAME_INDEX_Dead:
542       return false;
543     default:
544       return true;
545   }
546 }
547 
548 static bool
HasASCIIDigit(const ShortcutKeyCandidateArray & aCandidates)549 HasASCIIDigit(const ShortcutKeyCandidateArray& aCandidates)
550 {
551   for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
552     uint32_t ch = aCandidates[i].mCharCode;
553     if (ch >= '0' && ch <= '9')
554       return true;
555   }
556   return false;
557 }
558 
559 static bool
CharsCaseInsensitiveEqual(uint32_t aChar1,uint32_t aChar2)560 CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
561 {
562   return aChar1 == aChar2 ||
563          (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
564           ToLowerCase(static_cast<char16_t>(aChar1)) ==
565             ToLowerCase(static_cast<char16_t>(aChar2)));
566 }
567 
568 static bool
IsCaseChangeableChar(uint32_t aChar)569 IsCaseChangeableChar(uint32_t aChar)
570 {
571   return IS_IN_BMP(aChar) &&
572          ToLowerCase(static_cast<char16_t>(aChar)) !=
573            ToUpperCase(static_cast<char16_t>(aChar));
574 }
575 
576 void
GetShortcutKeyCandidates(ShortcutKeyCandidateArray & aCandidates)577 WidgetKeyboardEvent::GetShortcutKeyCandidates(
578                        ShortcutKeyCandidateArray& aCandidates)
579 {
580   MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
581 
582   // ShortcutKeyCandidate::mCharCode is a candidate charCode.
583   // ShortcutKeyCandidate::mIgnoreShift means the mCharCode should be tried to
584   // execute a command with/without shift key state. If this is TRUE, the
585   // shifted key state should be ignored. Otherwise, don't ignore the state.
586   // the priority of the charCodes are (shift key is not pressed):
587   //   0: PseudoCharCode()/false,
588   //   1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
589   // the priority of the charCodes are (shift key is pressed):
590   //   0: PseudoCharCode()/false,
591   //   1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
592   //   3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
593   uint32_t pseudoCharCode = PseudoCharCode();
594   if (pseudoCharCode) {
595     ShortcutKeyCandidate key(pseudoCharCode, false);
596     aCandidates.AppendElement(key);
597   }
598 
599   uint32_t len = mAlternativeCharCodes.Length();
600   if (!IsShift()) {
601     for (uint32_t i = 0; i < len; ++i) {
602       uint32_t ch = mAlternativeCharCodes[i].mUnshiftedCharCode;
603       if (!ch || ch == pseudoCharCode) {
604         continue;
605       }
606       ShortcutKeyCandidate key(ch, false);
607       aCandidates.AppendElement(key);
608     }
609     // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
610     // this keyboard layout is AZERTY or similar layout, probably.
611     // In this case, Accel+[0-9] should be accessible without shift key.
612     // However, the priority should be lowest.
613     if (!HasASCIIDigit(aCandidates)) {
614       for (uint32_t i = 0; i < len; ++i) {
615         uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode;
616         if (ch >= '0' && ch <= '9') {
617           ShortcutKeyCandidate key(ch, false);
618           aCandidates.AppendElement(key);
619           break;
620         }
621       }
622     }
623   } else {
624     for (uint32_t i = 0; i < len; ++i) {
625       uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode;
626       if (!ch) {
627         continue;
628       }
629 
630       if (ch != pseudoCharCode) {
631         ShortcutKeyCandidate key(ch, false);
632         aCandidates.AppendElement(key);
633       }
634 
635       // If the char is an alphabet, the shift key state should not be
636       // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
637 
638       // And checking the charCode is same as unshiftedCharCode too.
639       // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
640       uint32_t unshiftCh = mAlternativeCharCodes[i].mUnshiftedCharCode;
641       if (CharsCaseInsensitiveEqual(ch, unshiftCh)) {
642         continue;
643       }
644 
645       // On the Hebrew keyboard layout on Windows, the unshifted char is a
646       // localized character but the shifted char is a Latin alphabet,
647       // then, we should not execute without the shift state. See bug 433192.
648       if (IsCaseChangeableChar(ch)) {
649         continue;
650       }
651 
652       // Setting the alternative charCode candidates for retry without shift
653       // key state only when the shift key is pressed.
654       ShortcutKeyCandidate key(ch, true);
655       aCandidates.AppendElement(key);
656     }
657   }
658 
659   // Special case for "Space" key.  With some keyboard layouts, "Space" with
660   // or without Shift key causes non-ASCII space.  For such keyboard layouts,
661   // we should guarantee that the key press works as an ASCII white space key
662   // press.  However, if the space key is assigned to a function key, it
663   // shouldn't work as a space key.
664   if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
665       mCodeNameIndex == CODE_NAME_INDEX_Space && pseudoCharCode != ' ') {
666     ShortcutKeyCandidate spaceKey(' ', false);
667     aCandidates.AppendElement(spaceKey);
668   }
669 }
670 
671 void
GetAccessKeyCandidates(nsTArray<uint32_t> & aCandidates)672 WidgetKeyboardEvent::GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates)
673 {
674   MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
675 
676   // return the lower cased charCode candidates for access keys.
677   // the priority of the charCodes are:
678   //   0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
679   //   3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
680   if (mCharCode) {
681     uint32_t ch = mCharCode;
682     if (IS_IN_BMP(ch)) {
683       ch = ToLowerCase(static_cast<char16_t>(ch));
684     }
685     aCandidates.AppendElement(ch);
686   }
687   for (uint32_t i = 0; i < mAlternativeCharCodes.Length(); ++i) {
688     uint32_t ch[2] =
689       { mAlternativeCharCodes[i].mUnshiftedCharCode,
690         mAlternativeCharCodes[i].mShiftedCharCode };
691     for (uint32_t j = 0; j < 2; ++j) {
692       if (!ch[j]) {
693         continue;
694       }
695       if (IS_IN_BMP(ch[j])) {
696         ch[j] = ToLowerCase(static_cast<char16_t>(ch[j]));
697       }
698       // Don't append the mCharCode that was already appended.
699       if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex) {
700         aCandidates.AppendElement(ch[j]);
701       }
702     }
703   }
704   // Special case for "Space" key.  With some keyboard layouts, "Space" with
705   // or without Shift key causes non-ASCII space.  For such keyboard layouts,
706   // we should guarantee that the key press works as an ASCII white space key
707   // press.  However, if the space key is assigned to a function key, it
708   // shouldn't work as a space key.
709   if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
710       mCodeNameIndex == CODE_NAME_INDEX_Space && mCharCode != ' ') {
711     aCandidates.AppendElement(' ');
712   }
713   return;
714 }
715 
716 /* static */ void
Shutdown()717 WidgetKeyboardEvent::Shutdown()
718 {
719   delete sKeyNameIndexHashtable;
720   sKeyNameIndexHashtable = nullptr;
721   delete sCodeNameIndexHashtable;
722   sCodeNameIndexHashtable = nullptr;
723 }
724 
725 /* static */ void
GetDOMKeyName(KeyNameIndex aKeyNameIndex,nsAString & aKeyName)726 WidgetKeyboardEvent::GetDOMKeyName(KeyNameIndex aKeyNameIndex,
727                                    nsAString& aKeyName)
728 {
729   if (aKeyNameIndex >= KEY_NAME_INDEX_USE_STRING) {
730     aKeyName.Truncate();
731     return;
732   }
733 
734   MOZ_RELEASE_ASSERT(static_cast<size_t>(aKeyNameIndex) <
735                        ArrayLength(kKeyNames),
736                      "Illegal key enumeration value");
737   aKeyName = kKeyNames[aKeyNameIndex];
738 }
739 
740 /* static */ void
GetDOMCodeName(CodeNameIndex aCodeNameIndex,nsAString & aCodeName)741 WidgetKeyboardEvent::GetDOMCodeName(CodeNameIndex aCodeNameIndex,
742                                     nsAString& aCodeName)
743 {
744   if (aCodeNameIndex >= CODE_NAME_INDEX_USE_STRING) {
745     aCodeName.Truncate();
746     return;
747   }
748 
749   MOZ_RELEASE_ASSERT(static_cast<size_t>(aCodeNameIndex) <
750                        ArrayLength(kCodeNames),
751                      "Illegal physical code enumeration value");
752   aCodeName = kCodeNames[aCodeNameIndex];
753 }
754 
755 /* static */ KeyNameIndex
GetKeyNameIndex(const nsAString & aKeyValue)756 WidgetKeyboardEvent::GetKeyNameIndex(const nsAString& aKeyValue)
757 {
758   if (!sKeyNameIndexHashtable) {
759     sKeyNameIndexHashtable =
760       new KeyNameIndexHashtable(ArrayLength(kKeyNames));
761     for (size_t i = 0; i < ArrayLength(kKeyNames); i++) {
762       sKeyNameIndexHashtable->Put(nsDependentString(kKeyNames[i]),
763                                   static_cast<KeyNameIndex>(i));
764     }
765   }
766   KeyNameIndex result = KEY_NAME_INDEX_USE_STRING;
767   sKeyNameIndexHashtable->Get(aKeyValue, &result);
768   return result;
769 }
770 
771 /* static */ CodeNameIndex
GetCodeNameIndex(const nsAString & aCodeValue)772 WidgetKeyboardEvent::GetCodeNameIndex(const nsAString& aCodeValue)
773 {
774   if (!sCodeNameIndexHashtable) {
775     sCodeNameIndexHashtable =
776       new CodeNameIndexHashtable(ArrayLength(kCodeNames));
777     for (size_t i = 0; i < ArrayLength(kCodeNames); i++) {
778       sCodeNameIndexHashtable->Put(nsDependentString(kCodeNames[i]),
779                                    static_cast<CodeNameIndex>(i));
780     }
781   }
782   CodeNameIndex result = CODE_NAME_INDEX_USE_STRING;
783   sCodeNameIndexHashtable->Get(aCodeValue, &result);
784   return result;
785 }
786 
787 /* static */ const char*
GetCommandStr(Command aCommand)788 WidgetKeyboardEvent::GetCommandStr(Command aCommand)
789 {
790 #define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr
791   static const char* const kCommands[] = {
792     "" // CommandDoNothing
793 #include "mozilla/CommandList.h"
794   };
795 #undef NS_DEFINE_COMMAND
796 
797   MOZ_RELEASE_ASSERT(static_cast<size_t>(aCommand) < ArrayLength(kCommands),
798                      "Illegal command enumeration value");
799   return kCommands[aCommand];
800 }
801 
802 /* static */ uint32_t
ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex)803 WidgetKeyboardEvent::ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex)
804 {
805   // Following commented out cases are not defined in PhysicalKeyCodeNameList.h
806   // but are defined by D3E spec.  So, they should be uncommented when the
807   // code values are defined in the header.
808   switch (aCodeNameIndex) {
809     case CODE_NAME_INDEX_AltLeft:
810     case CODE_NAME_INDEX_ControlLeft:
811     case CODE_NAME_INDEX_OSLeft:
812     case CODE_NAME_INDEX_ShiftLeft:
813       return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
814     case CODE_NAME_INDEX_AltRight:
815     case CODE_NAME_INDEX_ControlRight:
816     case CODE_NAME_INDEX_OSRight:
817     case CODE_NAME_INDEX_ShiftRight:
818       return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
819     case CODE_NAME_INDEX_Numpad0:
820     case CODE_NAME_INDEX_Numpad1:
821     case CODE_NAME_INDEX_Numpad2:
822     case CODE_NAME_INDEX_Numpad3:
823     case CODE_NAME_INDEX_Numpad4:
824     case CODE_NAME_INDEX_Numpad5:
825     case CODE_NAME_INDEX_Numpad6:
826     case CODE_NAME_INDEX_Numpad7:
827     case CODE_NAME_INDEX_Numpad8:
828     case CODE_NAME_INDEX_Numpad9:
829     case CODE_NAME_INDEX_NumpadAdd:
830     case CODE_NAME_INDEX_NumpadBackspace:
831     case CODE_NAME_INDEX_NumpadClear:
832     case CODE_NAME_INDEX_NumpadClearEntry:
833     case CODE_NAME_INDEX_NumpadComma:
834     case CODE_NAME_INDEX_NumpadDecimal:
835     case CODE_NAME_INDEX_NumpadDivide:
836     case CODE_NAME_INDEX_NumpadEnter:
837     case CODE_NAME_INDEX_NumpadEqual:
838     case CODE_NAME_INDEX_NumpadMemoryAdd:
839     case CODE_NAME_INDEX_NumpadMemoryClear:
840     case CODE_NAME_INDEX_NumpadMemoryRecall:
841     case CODE_NAME_INDEX_NumpadMemoryStore:
842     case CODE_NAME_INDEX_NumpadMemorySubtract:
843     case CODE_NAME_INDEX_NumpadMultiply:
844     case CODE_NAME_INDEX_NumpadParenLeft:
845     case CODE_NAME_INDEX_NumpadParenRight:
846     case CODE_NAME_INDEX_NumpadSubtract:
847       return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
848     default:
849       return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
850   }
851 }
852 
853 /* static */ uint32_t
ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex)854 WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex)
855 {
856   switch (aKeyNameIndex) {
857     case KEY_NAME_INDEX_Cancel:
858       return nsIDOMKeyEvent::DOM_VK_CANCEL;
859     case KEY_NAME_INDEX_Help:
860       return nsIDOMKeyEvent::DOM_VK_HELP;
861     case KEY_NAME_INDEX_Backspace:
862       return nsIDOMKeyEvent::DOM_VK_BACK_SPACE;
863     case KEY_NAME_INDEX_Tab:
864       return nsIDOMKeyEvent::DOM_VK_TAB;
865     case KEY_NAME_INDEX_Clear:
866       return nsIDOMKeyEvent::DOM_VK_CLEAR;
867     case KEY_NAME_INDEX_Enter:
868       return nsIDOMKeyEvent::DOM_VK_RETURN;
869     case KEY_NAME_INDEX_Shift:
870       return nsIDOMKeyEvent::DOM_VK_SHIFT;
871     case KEY_NAME_INDEX_Control:
872       return nsIDOMKeyEvent::DOM_VK_CONTROL;
873     case KEY_NAME_INDEX_Alt:
874       return nsIDOMKeyEvent::DOM_VK_ALT;
875     case KEY_NAME_INDEX_Pause:
876       return nsIDOMKeyEvent::DOM_VK_PAUSE;
877     case KEY_NAME_INDEX_CapsLock:
878       return nsIDOMKeyEvent::DOM_VK_CAPS_LOCK;
879     case KEY_NAME_INDEX_Hiragana:
880     case KEY_NAME_INDEX_Katakana:
881     case KEY_NAME_INDEX_HiraganaKatakana:
882     case KEY_NAME_INDEX_KanaMode:
883       return nsIDOMKeyEvent::DOM_VK_KANA;
884     case KEY_NAME_INDEX_HangulMode:
885       return nsIDOMKeyEvent::DOM_VK_HANGUL;
886     case KEY_NAME_INDEX_Eisu:
887       return nsIDOMKeyEvent::DOM_VK_EISU;
888     case KEY_NAME_INDEX_JunjaMode:
889       return nsIDOMKeyEvent::DOM_VK_JUNJA;
890     case KEY_NAME_INDEX_FinalMode:
891       return nsIDOMKeyEvent::DOM_VK_FINAL;
892     case KEY_NAME_INDEX_HanjaMode:
893       return nsIDOMKeyEvent::DOM_VK_HANJA;
894     case KEY_NAME_INDEX_KanjiMode:
895       return nsIDOMKeyEvent::DOM_VK_KANJI;
896     case KEY_NAME_INDEX_Escape:
897       return nsIDOMKeyEvent::DOM_VK_ESCAPE;
898     case KEY_NAME_INDEX_Convert:
899       return nsIDOMKeyEvent::DOM_VK_CONVERT;
900     case KEY_NAME_INDEX_NonConvert:
901       return nsIDOMKeyEvent::DOM_VK_NONCONVERT;
902     case KEY_NAME_INDEX_Accept:
903       return nsIDOMKeyEvent::DOM_VK_ACCEPT;
904     case KEY_NAME_INDEX_ModeChange:
905       return nsIDOMKeyEvent::DOM_VK_MODECHANGE;
906     case KEY_NAME_INDEX_PageUp:
907       return nsIDOMKeyEvent::DOM_VK_PAGE_UP;
908     case KEY_NAME_INDEX_PageDown:
909       return nsIDOMKeyEvent::DOM_VK_PAGE_DOWN;
910     case KEY_NAME_INDEX_End:
911       return nsIDOMKeyEvent::DOM_VK_END;
912     case KEY_NAME_INDEX_Home:
913       return nsIDOMKeyEvent::DOM_VK_HOME;
914     case KEY_NAME_INDEX_ArrowLeft:
915       return nsIDOMKeyEvent::DOM_VK_LEFT;
916     case KEY_NAME_INDEX_ArrowUp:
917       return nsIDOMKeyEvent::DOM_VK_UP;
918     case KEY_NAME_INDEX_ArrowRight:
919       return nsIDOMKeyEvent::DOM_VK_RIGHT;
920     case KEY_NAME_INDEX_ArrowDown:
921       return nsIDOMKeyEvent::DOM_VK_DOWN;
922     case KEY_NAME_INDEX_Select:
923       return nsIDOMKeyEvent::DOM_VK_SELECT;
924     case KEY_NAME_INDEX_Print:
925       return nsIDOMKeyEvent::DOM_VK_PRINT;
926     case KEY_NAME_INDEX_Execute:
927       return nsIDOMKeyEvent::DOM_VK_EXECUTE;
928     case KEY_NAME_INDEX_PrintScreen:
929       return nsIDOMKeyEvent::DOM_VK_PRINTSCREEN;
930     case KEY_NAME_INDEX_Insert:
931       return nsIDOMKeyEvent::DOM_VK_INSERT;
932     case KEY_NAME_INDEX_Delete:
933       return nsIDOMKeyEvent::DOM_VK_DELETE;
934     case KEY_NAME_INDEX_OS:
935     // case KEY_NAME_INDEX_Super:
936     // case KEY_NAME_INDEX_Hyper:
937       return nsIDOMKeyEvent::DOM_VK_WIN;
938     case KEY_NAME_INDEX_ContextMenu:
939       return nsIDOMKeyEvent::DOM_VK_CONTEXT_MENU;
940     case KEY_NAME_INDEX_Standby:
941       return nsIDOMKeyEvent::DOM_VK_SLEEP;
942     case KEY_NAME_INDEX_F1:
943       return nsIDOMKeyEvent::DOM_VK_F1;
944     case KEY_NAME_INDEX_F2:
945       return nsIDOMKeyEvent::DOM_VK_F2;
946     case KEY_NAME_INDEX_F3:
947       return nsIDOMKeyEvent::DOM_VK_F3;
948     case KEY_NAME_INDEX_F4:
949       return nsIDOMKeyEvent::DOM_VK_F4;
950     case KEY_NAME_INDEX_F5:
951       return nsIDOMKeyEvent::DOM_VK_F5;
952     case KEY_NAME_INDEX_F6:
953       return nsIDOMKeyEvent::DOM_VK_F6;
954     case KEY_NAME_INDEX_F7:
955       return nsIDOMKeyEvent::DOM_VK_F7;
956     case KEY_NAME_INDEX_F8:
957       return nsIDOMKeyEvent::DOM_VK_F8;
958     case KEY_NAME_INDEX_F9:
959       return nsIDOMKeyEvent::DOM_VK_F9;
960     case KEY_NAME_INDEX_F10:
961       return nsIDOMKeyEvent::DOM_VK_F10;
962     case KEY_NAME_INDEX_F11:
963       return nsIDOMKeyEvent::DOM_VK_F11;
964     case KEY_NAME_INDEX_F12:
965       return nsIDOMKeyEvent::DOM_VK_F12;
966     case KEY_NAME_INDEX_F13:
967       return nsIDOMKeyEvent::DOM_VK_F13;
968     case KEY_NAME_INDEX_F14:
969       return nsIDOMKeyEvent::DOM_VK_F14;
970     case KEY_NAME_INDEX_F15:
971       return nsIDOMKeyEvent::DOM_VK_F15;
972     case KEY_NAME_INDEX_F16:
973       return nsIDOMKeyEvent::DOM_VK_F16;
974     case KEY_NAME_INDEX_F17:
975       return nsIDOMKeyEvent::DOM_VK_F17;
976     case KEY_NAME_INDEX_F18:
977       return nsIDOMKeyEvent::DOM_VK_F18;
978     case KEY_NAME_INDEX_F19:
979       return nsIDOMKeyEvent::DOM_VK_F19;
980     case KEY_NAME_INDEX_F20:
981       return nsIDOMKeyEvent::DOM_VK_F20;
982     case KEY_NAME_INDEX_F21:
983       return nsIDOMKeyEvent::DOM_VK_F21;
984     case KEY_NAME_INDEX_F22:
985       return nsIDOMKeyEvent::DOM_VK_F22;
986     case KEY_NAME_INDEX_F23:
987       return nsIDOMKeyEvent::DOM_VK_F23;
988     case KEY_NAME_INDEX_F24:
989       return nsIDOMKeyEvent::DOM_VK_F24;
990     case KEY_NAME_INDEX_NumLock:
991       return nsIDOMKeyEvent::DOM_VK_NUM_LOCK;
992     case KEY_NAME_INDEX_ScrollLock:
993       return nsIDOMKeyEvent::DOM_VK_SCROLL_LOCK;
994     case KEY_NAME_INDEX_AudioVolumeMute:
995       return nsIDOMKeyEvent::DOM_VK_VOLUME_MUTE;
996     case KEY_NAME_INDEX_AudioVolumeDown:
997       return nsIDOMKeyEvent::DOM_VK_VOLUME_DOWN;
998     case KEY_NAME_INDEX_AudioVolumeUp:
999       return nsIDOMKeyEvent::DOM_VK_VOLUME_UP;
1000     case KEY_NAME_INDEX_Meta:
1001       return nsIDOMKeyEvent::DOM_VK_META;
1002     case KEY_NAME_INDEX_AltGraph:
1003       return nsIDOMKeyEvent::DOM_VK_ALTGR;
1004     case KEY_NAME_INDEX_Attn:
1005       return nsIDOMKeyEvent::DOM_VK_ATTN;
1006     case KEY_NAME_INDEX_CrSel:
1007       return nsIDOMKeyEvent::DOM_VK_CRSEL;
1008     case KEY_NAME_INDEX_ExSel:
1009       return nsIDOMKeyEvent::DOM_VK_EXSEL;
1010     case KEY_NAME_INDEX_EraseEof:
1011       return nsIDOMKeyEvent::DOM_VK_EREOF;
1012     case KEY_NAME_INDEX_Play:
1013       return nsIDOMKeyEvent::DOM_VK_PLAY;
1014     case KEY_NAME_INDEX_ZoomToggle:
1015     case KEY_NAME_INDEX_ZoomIn:
1016     case KEY_NAME_INDEX_ZoomOut:
1017       return nsIDOMKeyEvent::DOM_VK_ZOOM;
1018     default:
1019       return 0;
1020   }
1021 }
1022 
1023 /* static */ Modifier
GetModifierForKeyName(KeyNameIndex aKeyNameIndex)1024 WidgetKeyboardEvent::GetModifierForKeyName(KeyNameIndex aKeyNameIndex)
1025 {
1026   switch (aKeyNameIndex) {
1027     case KEY_NAME_INDEX_Alt:
1028       return MODIFIER_ALT;
1029     case KEY_NAME_INDEX_AltGraph:
1030       return MODIFIER_ALTGRAPH;
1031     case KEY_NAME_INDEX_CapsLock:
1032       return MODIFIER_CAPSLOCK;
1033     case KEY_NAME_INDEX_Control:
1034       return MODIFIER_CONTROL;
1035     case KEY_NAME_INDEX_Fn:
1036       return MODIFIER_FN;
1037     case KEY_NAME_INDEX_FnLock:
1038       return MODIFIER_FNLOCK;
1039     // case KEY_NAME_INDEX_Hyper:
1040     case KEY_NAME_INDEX_Meta:
1041       return MODIFIER_META;
1042     case KEY_NAME_INDEX_NumLock:
1043       return MODIFIER_NUMLOCK;
1044     case KEY_NAME_INDEX_OS:
1045       return MODIFIER_OS;
1046     case KEY_NAME_INDEX_ScrollLock:
1047       return MODIFIER_SCROLLLOCK;
1048     case KEY_NAME_INDEX_Shift:
1049       return MODIFIER_SHIFT;
1050     // case KEY_NAME_INDEX_Super:
1051     case KEY_NAME_INDEX_Symbol:
1052       return MODIFIER_SYMBOL;
1053     case KEY_NAME_INDEX_SymbolLock:
1054       return MODIFIER_SYMBOLLOCK;
1055     default:
1056       return MODIFIER_NONE;
1057   }
1058 }
1059 
1060 /* static */ bool
IsLockableModifier(KeyNameIndex aKeyNameIndex)1061 WidgetKeyboardEvent::IsLockableModifier(KeyNameIndex aKeyNameIndex)
1062 {
1063   switch (aKeyNameIndex) {
1064     case KEY_NAME_INDEX_CapsLock:
1065     case KEY_NAME_INDEX_FnLock:
1066     case KEY_NAME_INDEX_NumLock:
1067     case KEY_NAME_INDEX_ScrollLock:
1068     case KEY_NAME_INDEX_SymbolLock:
1069       return true;
1070     default:
1071       return false;
1072   }
1073 }
1074 
1075 } // namespace mozilla
1076