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