Bug 1942006 - Upstream a variety of Servo-specific code from Servo's downstream fork...
[gecko.git] / widget / WidgetEventImpl.cpp
blob39d9623bc79466b0419afb0357a33d39ce9c3d68
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/. */
6 #include "BasicEvents.h"
7 #include "ContentEvents.h"
8 #include "MiscEvents.h"
9 #include "MouseEvents.h"
10 #include "NativeKeyBindingsType.h"
11 #include "TextEventDispatcher.h"
12 #include "TextEvents.h"
13 #include "TouchEvents.h"
15 #include "mozilla/EventForwards.h"
16 #include "mozilla/EventStateManager.h"
17 #include "mozilla/InternalMutationEvent.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/StaticPrefs_dom.h"
21 #include "mozilla/StaticPrefs_mousewheel.h"
22 #include "mozilla/StaticPrefs_ui.h"
23 #include "mozilla/WritingModes.h"
24 #include "mozilla/dom/KeyboardEventBinding.h"
25 #include "mozilla/dom/MouseEventBinding.h"
26 #include "mozilla/dom/WheelEventBinding.h"
27 #include "nsCommandParams.h"
28 #include "nsContentUtils.h"
29 #include "nsIContent.h"
30 #include "nsIDragSession.h"
31 #include "nsMathUtils.h"
32 #include "nsPrintfCString.h"
34 #if defined(XP_WIN)
35 # include "windef.h"
36 # include "winnetwk.h"
37 # include "npapi.h"
38 # include "WinUtils.h"
39 #endif // #if defined (XP_WIN)
41 #if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
42 # include "NativeKeyBindings.h"
43 #endif // #if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
45 namespace mozilla {
47 /******************************************************************************
48 * Global helper methods
49 ******************************************************************************/
51 const char* ToChar(EventMessage aEventMessage) {
52 switch (aEventMessage) {
53 #define NS_EVENT_MESSAGE(aMessage) \
54 case aMessage: \
55 return #aMessage;
57 #include "mozilla/EventMessageList.h"
59 #undef NS_EVENT_MESSAGE
60 default:
61 return "illegal event message";
65 bool IsPointerEventMessage(EventMessage aMessage) {
66 switch (aMessage) {
67 case ePointerDown:
68 case ePointerMove:
69 case ePointerUp:
70 case ePointerCancel:
71 case ePointerOver:
72 case ePointerOut:
73 case ePointerEnter:
74 case ePointerLeave:
75 case ePointerGotCapture:
76 case ePointerLostCapture:
77 case ePointerClick:
78 case ePointerAuxClick:
79 case eContextMenu:
80 return true;
81 default:
82 return false;
86 bool IsPointerEventMessageOriginallyMouseEventMessage(EventMessage aMessage) {
87 return aMessage == ePointerClick || aMessage == ePointerAuxClick ||
88 aMessage == eContextMenu;
91 bool IsForbiddenDispatchingToNonElementContent(EventMessage aMessage) {
92 switch (aMessage) {
93 // Keyboard event target should be an Element node
94 case eKeyDown:
95 case eKeyUp:
96 case eKeyPress:
97 // Mouse event target should be an Element node
98 case eMouseMove:
99 case eMouseUp:
100 case eMouseDown:
101 case eMouseEnterIntoWidget:
102 case eMouseExitFromWidget:
103 case eMouseDoubleClick:
104 case eMouseActivate:
105 case eMouseOver:
106 case eMouseOut:
107 case eMouseHitTest:
108 case eMouseEnter:
109 case eMouseLeave:
110 case eMouseTouchDrag:
111 case eMouseLongTap:
112 case eMouseExploreByTouch:
113 // Pointer event target should be an Element node
114 case ePointerClick:
115 case ePointerAuxClick:
116 case ePointerMove:
117 case ePointerUp:
118 case ePointerDown:
119 case ePointerOver:
120 case ePointerOut:
121 case ePointerEnter:
122 case ePointerLeave:
123 case ePointerCancel:
124 case ePointerGotCapture:
125 case ePointerLostCapture:
126 case eContextMenu:
127 // Drag event target should be an Element node
128 case eDragEnter:
129 case eDragOver:
130 case eDragExit:
131 case eDrag:
132 case eDragEnd:
133 case eDragStart:
134 case eDrop:
135 case eDragLeave:
136 case eQueryDropTargetHittest:
137 // case mouse wheel related message target should be an Element node
138 case eLegacyMouseLineOrPageScroll:
139 case eLegacyMousePixelScroll:
140 case eWheel:
141 // Composition event message target should be an Element node
142 case eCompositionStart:
143 case eCompositionEnd:
144 case eCompositionUpdate:
145 case eCompositionChange:
146 case eCompositionCommitAsIs:
147 case eCompositionCommit:
148 case eCompositionCommitRequestHandled:
149 // Gesture event target should be an Element node
150 case eSwipeGestureMayStart:
151 case eSwipeGestureStart:
152 case eSwipeGestureUpdate:
153 case eSwipeGestureEnd:
154 case eSwipeGesture:
155 case eMagnifyGestureStart:
156 case eMagnifyGestureUpdate:
157 case eMagnifyGesture:
158 case eRotateGestureStart:
159 case eRotateGestureUpdate:
160 case eRotateGesture:
161 case eTapGesture:
162 case ePressTapGesture:
163 case eEdgeUIStarted:
164 case eEdgeUICanceled:
165 case eEdgeUICompleted:
166 // Touch event target should be an Element node
167 case eTouchStart:
168 case eTouchMove:
169 case eTouchEnd:
170 case eTouchCancel:
171 case eTouchPointerCancel:
172 return true;
174 default:
175 return false;
179 const char* ToChar(EventClassID aEventClassID) {
180 switch (aEventClassID) {
181 #define NS_ROOT_EVENT_CLASS(aPrefix, aName) \
182 case eBasic##aName##Class: \
183 return "eBasic" #aName "Class";
185 #define NS_EVENT_CLASS(aPrefix, aName) \
186 case e##aName##Class: \
187 return "e" #aName "Class";
189 #include "mozilla/EventClassList.h"
191 #undef NS_EVENT_CLASS
192 #undef NS_ROOT_EVENT_CLASS
193 default:
194 return "illegal event class ID";
198 const nsCString ToString(KeyNameIndex aKeyNameIndex) {
199 if (aKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
200 return "USE_STRING"_ns;
202 nsAutoString keyName;
203 WidgetKeyboardEvent::GetDOMKeyName(aKeyNameIndex, keyName);
204 return NS_ConvertUTF16toUTF8(keyName);
207 const nsCString ToString(CodeNameIndex aCodeNameIndex) {
208 if (aCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
209 return "USE_STRING"_ns;
211 nsAutoString codeName;
212 WidgetKeyboardEvent::GetDOMCodeName(aCodeNameIndex, codeName);
213 return NS_ConvertUTF16toUTF8(codeName);
216 const char* ToChar(Command aCommand) {
217 if (aCommand == Command::DoNothing) {
218 return "CommandDoNothing";
221 switch (aCommand) {
222 #define NS_DEFINE_COMMAND(aName, aCommandStr) \
223 case Command::aName: \
224 return "Command::" #aName;
225 #define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam) \
226 case Command::aName: \
227 return "Command::" #aName;
228 #define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) \
229 case Command::aName: \
230 return "Command::" #aName;
232 #include "mozilla/CommandList.h"
234 #undef NS_DEFINE_COMMAND
235 #undef NS_DEFINE_COMMAND_WITH_PARAM
236 #undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND
238 default:
239 return "illegal command value";
243 const nsCString GetDOMKeyCodeName(uint32_t aKeyCode) {
244 switch (aKeyCode) {
245 #define NS_DISALLOW_SAME_KEYCODE
246 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
247 case aDOMKeyCode: \
248 return nsLiteralCString(#aDOMKeyName);
250 #include "mozilla/VirtualKeyCodeList.h"
252 #undef NS_DEFINE_VK
253 #undef NS_DISALLOW_SAME_KEYCODE
255 default:
256 return nsPrintfCString("Invalid DOM keyCode (0x%08X)", aKeyCode);
260 /******************************************************************************
261 * non class method implementation
262 ******************************************************************************/
264 static nsTHashMap<nsDepCharHashKey, Command>* sCommandHashtable = nullptr;
266 Command GetInternalCommand(const char* aCommandName,
267 const nsCommandParams* aCommandParams) {
268 if (!aCommandName) {
269 return Command::DoNothing;
272 // Special cases for "cmd_align". It's mapped to multiple internal commands
273 // with additional param. Therefore, we cannot handle it with the hashtable.
274 if (!strcmp(aCommandName, "cmd_align")) {
275 if (!aCommandParams) {
276 // Note that if this is called by EditorCommand::IsCommandEnabled(),
277 // it cannot set aCommandParams. So, don't warn in this case even though
278 // this is illegal case for DoCommandParams().
279 return Command::FormatJustify;
281 nsAutoCString cValue;
282 nsresult rv = aCommandParams->GetCString("state_attribute", cValue);
283 if (NS_FAILED(rv)) {
284 nsString value; // Avoid copying the string buffer with using nsString.
285 rv = aCommandParams->GetString("state_attribute", value);
286 if (NS_FAILED(rv)) {
287 return Command::FormatJustifyNone;
289 CopyUTF16toUTF8(value, cValue);
291 if (cValue.LowerCaseEqualsASCII("left")) {
292 return Command::FormatJustifyLeft;
294 if (cValue.LowerCaseEqualsASCII("right")) {
295 return Command::FormatJustifyRight;
297 if (cValue.LowerCaseEqualsASCII("center")) {
298 return Command::FormatJustifyCenter;
300 if (cValue.LowerCaseEqualsASCII("justify")) {
301 return Command::FormatJustifyFull;
303 if (cValue.IsEmpty()) {
304 return Command::FormatJustifyNone;
306 return Command::DoNothing;
309 if (!sCommandHashtable) {
310 sCommandHashtable = new nsTHashMap<nsDepCharHashKey, Command>();
311 #define NS_DEFINE_COMMAND(aName, aCommandStr) \
312 sCommandHashtable->InsertOrUpdate(#aCommandStr, Command::aName);
314 #define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam)
316 #define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName)
318 #include "mozilla/CommandList.h"
320 #undef NS_DEFINE_COMMAND
321 #undef NS_DEFINE_COMMAND_WITH_PARAM
322 #undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND
324 Command command = Command::DoNothing;
325 if (!sCommandHashtable->Get(aCommandName, &command)) {
326 return Command::DoNothing;
328 return command;
331 /******************************************************************************
332 * As*Event() implementation
333 ******************************************************************************/
335 #define NS_ROOT_EVENT_CLASS(aPrefix, aName)
336 #define NS_EVENT_CLASS(aPrefix, aName) \
337 aPrefix##aName* WidgetEvent::As##aName() { return nullptr; } \
339 const aPrefix##aName* WidgetEvent::As##aName() const { \
340 return const_cast<WidgetEvent*>(this)->As##aName(); \
343 #include "mozilla/EventClassList.h"
345 #undef NS_EVENT_CLASS
346 #undef NS_ROOT_EVENT_CLASS
348 /******************************************************************************
349 * mozilla::WidgetEvent
351 * Event struct type checking methods.
352 ******************************************************************************/
354 bool WidgetEvent::IsQueryContentEvent() const {
355 return mClass == eQueryContentEventClass;
358 bool WidgetEvent::IsSelectionEvent() const {
359 return mClass == eSelectionEventClass;
362 bool WidgetEvent::IsContentCommandEvent() const {
363 return mClass == eContentCommandEventClass;
366 /******************************************************************************
367 * mozilla::WidgetEvent
369 * Event message checking methods.
370 ******************************************************************************/
372 bool WidgetEvent::HasMouseEventMessage() const {
373 switch (mMessage) {
374 case eMouseDown:
375 case eMouseUp:
376 case eMouseDoubleClick:
377 case eMouseEnterIntoWidget:
378 case eMouseExitFromWidget:
379 case eMouseActivate:
380 case eMouseOver:
381 case eMouseOut:
382 case eMouseHitTest:
383 case eMouseMove:
384 return true;
385 // TODO: Perhaps, we should rename this method.
386 case ePointerClick:
387 case ePointerAuxClick:
388 return true;
389 default:
390 return false;
394 bool WidgetEvent::IsMouseEventClassOrHasClickRelatedPointerEvent() const {
395 return mClass == eMouseEventClass ||
396 IsPointerEventMessageOriginallyMouseEventMessage(mMessage);
399 bool WidgetEvent::HasDragEventMessage() const {
400 switch (mMessage) {
401 case eDragEnter:
402 case eDragOver:
403 case eDragExit:
404 case eDrag:
405 case eDragEnd:
406 case eDragStart:
407 case eDrop:
408 case eDragLeave:
409 return true;
410 default:
411 return false;
415 /* static */
416 bool WidgetEvent::IsKeyEventMessage(EventMessage aMessage) {
417 switch (aMessage) {
418 case eKeyDown:
419 case eKeyPress:
420 case eKeyUp:
421 case eAccessKeyNotFound:
422 return true;
423 default:
424 return false;
428 bool WidgetEvent::HasIMEEventMessage() const {
429 switch (mMessage) {
430 case eCompositionStart:
431 case eCompositionEnd:
432 case eCompositionUpdate:
433 case eCompositionChange:
434 case eCompositionCommitAsIs:
435 case eCompositionCommit:
436 return true;
437 default:
438 return false;
442 /******************************************************************************
443 * mozilla::WidgetEvent
445 * Specific event checking methods.
446 ******************************************************************************/
448 bool WidgetEvent::CanBeSentToRemoteProcess() const {
449 // If this event is explicitly marked as shouldn't be sent to remote process,
450 // just return false.
451 if (IsCrossProcessForwardingStopped()) {
452 return false;
455 if (mClass == eKeyboardEventClass || mClass == eWheelEventClass) {
456 return true;
459 switch (mMessage) {
460 case eMouseDown:
461 case eMouseUp:
462 case eMouseMove:
463 case eMouseExploreByTouch:
464 case eContextMenu:
465 case eMouseEnterIntoWidget:
466 case eMouseExitFromWidget:
467 case eMouseTouchDrag:
468 case eTouchStart:
469 case eTouchMove:
470 case eTouchEnd:
471 case eTouchCancel:
472 case eDragOver:
473 case eDragExit:
474 case eDrop:
475 return true;
476 default:
477 return false;
481 bool WidgetEvent::WillBeSentToRemoteProcess() const {
482 // This event won't be posted to remote process if it's already explicitly
483 // stopped.
484 if (IsCrossProcessForwardingStopped()) {
485 return false;
488 // When mOriginalTarget is nullptr, this method shouldn't be used.
489 if (NS_WARN_IF(!mOriginalTarget)) {
490 return false;
493 return EventStateManager::IsTopLevelRemoteTarget(
494 nsIContent::FromEventTarget(mOriginalTarget));
497 bool WidgetEvent::IsIMERelatedEvent() const {
498 return HasIMEEventMessage() ||
499 (IsQueryContentEvent() && mMessage != eQueryDropTargetHittest) ||
500 IsSelectionEvent();
503 bool WidgetEvent::IsUsingCoordinates() const {
504 const WidgetMouseEvent* mouseEvent = AsMouseEvent();
505 if (mouseEvent) {
506 return !mouseEvent->IsContextMenuKeyEvent();
508 return !HasKeyEventMessage() && !IsIMERelatedEvent() &&
509 !IsContentCommandEvent();
512 bool WidgetEvent::IsTargetedAtFocusedWindow() const {
513 const WidgetMouseEvent* mouseEvent = AsMouseEvent();
514 if (mouseEvent) {
515 return mouseEvent->IsContextMenuKeyEvent();
517 return HasKeyEventMessage() || IsIMERelatedEvent() || IsContentCommandEvent();
520 bool WidgetEvent::IsTargetedAtFocusedContent() const {
521 const WidgetMouseEvent* mouseEvent = AsMouseEvent();
522 if (mouseEvent) {
523 return mouseEvent->IsContextMenuKeyEvent();
525 return HasKeyEventMessage() || IsIMERelatedEvent();
528 bool WidgetEvent::IsAllowedToDispatchDOMEvent() const {
529 switch (mClass) {
530 case eMouseEventClass:
531 if (mMessage == eMouseTouchDrag) {
532 return false;
534 [[fallthrough]];
535 case ePointerEventClass:
536 // We want synthesized mouse moves to cause mouseover and mouseout
537 // DOM events (EventStateManager::PreHandleEvent), but not mousemove
538 // DOM events.
539 // Synthesized button up events also do not cause DOM events because they
540 // do not have a reliable mRefPoint.
541 return AsMouseEvent()->mReason == WidgetMouseEvent::eReal;
543 case eWheelEventClass: {
544 // wheel event whose all delta values are zero by user pref applied, it
545 // shouldn't cause a DOM event.
546 const WidgetWheelEvent* wheelEvent = AsWheelEvent();
547 return wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0 ||
548 wheelEvent->mDeltaZ != 0.0;
550 case eTouchEventClass:
551 return mMessage != eTouchPointerCancel;
552 // Following events are handled in EventStateManager, so, we don't need to
553 // dispatch DOM event for them into the DOM tree.
554 case eQueryContentEventClass:
555 case eSelectionEventClass:
556 case eContentCommandEventClass:
557 return false;
559 default:
560 return true;
564 bool WidgetEvent::IsAllowedToDispatchInSystemGroup() const {
565 // We don't expect to implement default behaviors with pointer events because
566 // if we do, prevent default on mouse events can't prevent default behaviors
567 // anymore.
568 return mClass != ePointerEventClass ||
569 IsPointerEventMessageOriginallyMouseEventMessage(mMessage);
572 bool WidgetEvent::IsBlockedForFingerprintingResistance() const {
573 switch (mClass) {
574 case eKeyboardEventClass: {
575 const WidgetKeyboardEvent* keyboardEvent = AsKeyboardEvent();
577 return (keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Alt ||
578 keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Shift ||
579 keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control ||
580 keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph);
582 case ePointerEventClass: {
583 if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) {
584 return false;
587 if (SPOOFED_MAX_TOUCH_POINTS > 0) {
588 return false;
591 const WidgetPointerEvent* pointerEvent = AsPointerEvent();
593 // We suppress the pointer events if it is not primary for fingerprinting
594 // resistance. It is because of that we want to spoof any pointer event
595 // into a mouse pointer event and the mouse pointer event only has
596 // isPrimary as true.
597 return !pointerEvent->mIsPrimary;
599 default:
600 return false;
604 bool WidgetEvent::AllowFlushingPendingNotifications() const {
605 if (mClass != eQueryContentEventClass) {
606 return true;
608 // If the dispatcher does not want a flush of pending notifications, it may
609 // be caused by that it's unsafe. Therefore, we should allow handlers to
610 // flush pending things only when the dispatcher requires the latest content
611 // layout.
612 return AsQueryContentEvent()->mNeedsToFlushLayout;
615 bool WidgetEvent::ShouldIgnoreCapturingContent() const {
616 MOZ_ASSERT(IsUsingCoordinates());
618 if (MOZ_UNLIKELY(!IsTrusted())) {
619 return false;
621 return mClass == eMouseEventClass || mClass == ePointerEventClass
622 ? AsMouseEvent()->mIgnoreCapturingContent
623 : false;
626 /******************************************************************************
627 * mozilla::WidgetEvent
629 * Misc methods.
630 ******************************************************************************/
632 static dom::EventTarget* GetTargetForDOMEvent(dom::EventTarget* aTarget) {
633 return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr;
636 dom::EventTarget* WidgetEvent::GetDOMEventTarget() const {
637 return GetTargetForDOMEvent(mTarget);
640 dom::EventTarget* WidgetEvent::GetCurrentDOMEventTarget() const {
641 return GetTargetForDOMEvent(mCurrentTarget);
644 dom::EventTarget* WidgetEvent::GetOriginalDOMEventTarget() const {
645 if (mOriginalTarget) {
646 return GetTargetForDOMEvent(mOriginalTarget);
648 return GetDOMEventTarget();
651 void WidgetEvent::PreventDefault(bool aCalledByDefaultHandler,
652 nsIPrincipal* aPrincipal) {
653 if (mMessage == ePointerDown) {
654 if (aCalledByDefaultHandler) {
655 // Shouldn't prevent default on pointerdown by default handlers to stop
656 // firing legacy mouse events. Use MOZ_ASSERT to catch incorrect usages
657 // in debug builds.
658 MOZ_ASSERT(false);
659 return;
661 if (aPrincipal) {
662 nsAutoString addonId;
663 Unused << NS_WARN_IF(NS_FAILED(aPrincipal->GetAddonId(addonId)));
664 if (!addonId.IsEmpty()) {
665 // Ignore the case that it's called by a web extension.
666 return;
670 mFlags.PreventDefault(aCalledByDefaultHandler);
673 bool WidgetEvent::IsUserAction() const {
674 if (!IsTrusted()) {
675 return false;
677 // FYI: eMouseScrollEventClass and ePointerEventClass represent
678 // user action but they are synthesized events.
679 switch (mClass) {
680 case eKeyboardEventClass:
681 case eCompositionEventClass:
682 case eMouseScrollEventClass:
683 case eWheelEventClass:
684 case eGestureNotifyEventClass:
685 case eSimpleGestureEventClass:
686 case eTouchEventClass:
687 case eCommandEventClass:
688 case eContentCommandEventClass:
689 return true;
690 case eMouseEventClass:
691 case eDragEventClass:
692 case ePointerEventClass:
693 return AsMouseEvent()->IsReal();
694 default:
695 return false;
699 /******************************************************************************
700 * mozilla::WidgetInputEvent
701 ******************************************************************************/
703 /* static */
704 Modifier WidgetInputEvent::GetModifier(const nsAString& aDOMKeyName) {
705 if (aDOMKeyName.EqualsLiteral("Accel")) {
706 return AccelModifier();
708 KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aDOMKeyName);
709 return WidgetKeyboardEvent::GetModifierForKeyName(keyNameIndex);
712 /* static */
713 Modifier WidgetInputEvent::AccelModifier() {
714 static Modifier sAccelModifier = MODIFIER_NONE;
715 if (sAccelModifier == MODIFIER_NONE) {
716 switch (StaticPrefs::ui_key_accelKey()) {
717 case dom::KeyboardEvent_Binding::DOM_VK_META:
718 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
719 sAccelModifier = MODIFIER_META;
720 break;
721 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
722 sAccelModifier = MODIFIER_ALT;
723 break;
724 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
725 sAccelModifier = MODIFIER_CONTROL;
726 break;
727 default:
728 #ifdef XP_MACOSX
729 sAccelModifier = MODIFIER_META;
730 #else
731 sAccelModifier = MODIFIER_CONTROL;
732 #endif
733 break;
736 return sAccelModifier;
739 /******************************************************************************
740 * mozilla::WidgetPointerHelper (MouseEvents.h)
741 ******************************************************************************/
743 // static
744 int32_t WidgetPointerHelper::GetValidTiltValue(int32_t aTilt) {
745 if (MOZ_LIKELY(aTilt >= -90 && aTilt <= 90)) {
746 return aTilt;
748 while (aTilt > 90) {
749 aTilt -= 180;
751 while (aTilt < -90) {
752 aTilt += 180;
754 MOZ_ASSERT(aTilt >= -90 && aTilt <= 90);
755 return aTilt;
758 // static
759 double WidgetPointerHelper::GetValidAltitudeAngle(double aAltitudeAngle) {
760 if (MOZ_LIKELY(aAltitudeAngle >= 0.0 && aAltitudeAngle <= kHalfPi)) {
761 return aAltitudeAngle;
763 while (aAltitudeAngle > kHalfPi) {
764 aAltitudeAngle -= kHalfPi;
766 while (aAltitudeAngle < 0.0) {
767 aAltitudeAngle += kHalfPi;
769 MOZ_ASSERT(aAltitudeAngle >= 0.0 && aAltitudeAngle <= kHalfPi);
770 return aAltitudeAngle;
773 // static
774 double WidgetPointerHelper::GetValidAzimuthAngle(double aAzimuthAngle) {
775 if (MOZ_LIKELY(aAzimuthAngle >= 0.0 && aAzimuthAngle <= kDoublePi)) {
776 return aAzimuthAngle;
778 while (aAzimuthAngle > kDoublePi) {
779 aAzimuthAngle -= kDoublePi;
781 while (aAzimuthAngle < 0.0) {
782 aAzimuthAngle += kDoublePi;
784 MOZ_ASSERT(aAzimuthAngle >= 0.0 && aAzimuthAngle <= kDoublePi);
785 return aAzimuthAngle;
788 // static
789 double WidgetPointerHelper::ComputeAltitudeAngle(int32_t aTiltX,
790 int32_t aTiltY) {
791 // https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
792 aTiltX = GetValidTiltValue(aTiltX);
793 aTiltY = GetValidTiltValue(aTiltY);
794 if (std::abs(aTiltX) == 90 || std::abs(aTiltY) == 90) {
795 return 0.0;
797 const double tiltXRadians = kPi / 180.0 * aTiltX;
798 const double tiltYRadians = kPi / 180.0 * aTiltY;
799 if (!aTiltX) {
800 return kHalfPi - std::abs(tiltYRadians);
802 if (!aTiltY) {
803 return kHalfPi - std::abs(tiltXRadians);
805 return std::atan(1.0 /
806 NS_hypot(std::tan(tiltXRadians), std::tan(tiltYRadians)));
809 // static
810 double WidgetPointerHelper::ComputeAzimuthAngle(int32_t aTiltX,
811 int32_t aTiltY) {
812 // https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
813 aTiltX = GetValidTiltValue(aTiltX);
814 aTiltY = GetValidTiltValue(aTiltY);
815 if (!aTiltX) {
816 if (aTiltY > 0) {
817 return kHalfPi;
819 return aTiltY < 0 ? 3.0 * kHalfPi : 0.0;
822 if (!aTiltY) {
823 return aTiltX < 0 ? kPi : 0.0;
826 if (std::abs(aTiltX) == 90 || std::abs(aTiltY) == 90) {
827 return 0.0;
830 const double tiltXRadians = kPi / 180.0 * aTiltX;
831 const double tiltYRadians = kPi / 180.0 * aTiltY;
832 const double azimuthAngle =
833 std::atan2(std::tan(tiltYRadians), std::tan(tiltXRadians));
834 return azimuthAngle < 0 ? azimuthAngle + kDoublePi : azimuthAngle;
837 // static
838 double WidgetPointerHelper::ComputeTiltX(double aAltitudeAngle,
839 double aAzimuthAngle) {
840 // https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
841 aAltitudeAngle = GetValidAltitudeAngle(aAltitudeAngle);
842 aAzimuthAngle = GetValidAzimuthAngle(aAzimuthAngle);
843 if (aAltitudeAngle == 0.0) {
844 if ((aAzimuthAngle >= 0.0 && aAzimuthAngle < kHalfPi) ||
845 (aAzimuthAngle > 3 * kHalfPi && aAzimuthAngle <= kDoublePi)) {
846 return 90; // pi / 2 * 180 / pi
848 if (aAzimuthAngle > kHalfPi && aAzimuthAngle < 3 * kHalfPi) {
849 return -90; // -1 * pi / 2 * 180 / pi
851 MOZ_ASSERT(aAzimuthAngle == kHalfPi || aAzimuthAngle == 3 * kHalfPi);
852 return 0.0;
855 constexpr double radToDeg = 180.0 / kPi;
856 return std::floor(
857 std::atan(std::cos(aAzimuthAngle) / std::tan(aAltitudeAngle)) * radToDeg +
858 0.5);
861 // static
862 double WidgetPointerHelper::ComputeTiltY(double aAltitudeAngle,
863 double aAzimuthAngle) {
864 // https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
865 aAltitudeAngle = GetValidAltitudeAngle(aAltitudeAngle);
866 aAzimuthAngle = GetValidAzimuthAngle(aAzimuthAngle);
867 if (aAltitudeAngle == 0.0) {
868 if (aAzimuthAngle > 0.0 && aAzimuthAngle < kPi) {
869 return 90; // pi / 2 * 180 / pi
871 if (aAzimuthAngle > kPi && aAzimuthAngle < kDoublePi) {
872 return -90; // -1 * pi / 2 * 180 / pi
874 MOZ_ASSERT(aAzimuthAngle == 0.0 || aAzimuthAngle == kPi ||
875 aAzimuthAngle == kDoublePi);
876 return 0.0;
878 constexpr double radToDeg = 180.0 / kPi;
879 return std::floor(
880 std::atan(std::sin(aAzimuthAngle) / std::tan(aAltitudeAngle)) * radToDeg +
881 0.5);
884 /******************************************************************************
885 * mozilla::WidgetMouseEventBase (MouseEvents.h)
886 ******************************************************************************/
888 bool WidgetMouseEventBase::InputSourceSupportsHover() const {
889 switch (mInputSource) {
890 case dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE:
891 case dom::MouseEvent_Binding::MOZ_SOURCE_PEN:
892 case dom::MouseEvent_Binding::MOZ_SOURCE_ERASER:
893 return true;
894 case dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH:
895 case dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN:
896 case dom::MouseEvent_Binding::MOZ_SOURCE_KEYBOARD:
897 case dom::MouseEvent_Binding::MOZ_SOURCE_CURSOR:
898 default:
899 return false;
903 bool WidgetMouseEventBase::DOMEventShouldUseFractionalCoords() const {
904 if (!StaticPrefs::dom_event_pointer_fractional_coordinates_enabled()) {
905 return false; // We completely don't support fractional coordinates
907 // If we support fractional coordinates only for PointerEvent, the spec
908 // recommend that `click`, `auxclick` and `contextmenu` keep using integer
909 // coordinates.
910 // https://w3c.github.io/pointerevents/#event-coordinates
911 if (mClass == ePointerEventClass && mMessage != ePointerClick &&
912 mMessage != ePointerAuxClick && mMessage != eContextMenu) {
913 return true;
915 // Untrusted events can be initialized with double values. However, Chrome
916 // returns integer coordinates for non-PointerEvent instances, `click`,
917 // `auxclick` and `contextmenu`. Therefore, it may be risky to allow
918 // fractional coordinates for all untrusted events right now because web apps
919 // may initialize untrusted events with quotients.
920 // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/events/pointer_event.h;l=59-91;drc=80c2637874588837a2d656dbd79ad8f227dc67e8
921 // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/events/pointer_event.cc;l=110-117;drc=8e948282d37c0e119e3102236878d6f4d5052c16
922 if (!IsTrusted()) {
923 return StaticPrefs::
924 dom_event_mouse_fractional_coordinates_untrusted_enabled();
926 // CSSOM suggested that MouseEvent interface can treat fractional values in
927 // all instances. However, it's risky for backward compatibility. Therefore,
928 // we don't have a plan to enable it for now.
929 return MOZ_UNLIKELY(
930 StaticPrefs::dom_event_mouse_fractional_coordinates_trusted_enabled());
933 /******************************************************************************
934 * mozilla::WidgetMouseEvent (MouseEvents.h)
935 ******************************************************************************/
937 /* static */
938 bool WidgetMouseEvent::IsMiddleClickPasteEnabled() {
939 return Preferences::GetBool("middlemouse.paste", false);
942 #ifdef DEBUG
943 void WidgetMouseEvent::AssertContextMenuEventButtonConsistency() const {
944 if (mMessage != eContextMenu) {
945 return;
948 if (mInputSource == dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
949 NS_WARNING_ASSERTION(mButton == MouseButton::ePrimary,
950 "eContextMenu events by touch trigger should use "
951 "primary mouse button / touch contact");
952 } else if (mContextMenuTrigger == eNormal) {
953 NS_WARNING_ASSERTION(mButton == MouseButton::eSecondary,
954 "eContextMenu events with eNormal trigger should use "
955 "secondary mouse button");
956 } else {
957 NS_WARNING_ASSERTION(mButton == MouseButton::ePrimary,
958 "eContextMenu events with non-eNormal trigger should "
959 "use primary mouse button");
962 if (mContextMenuTrigger == eControlClick) {
963 NS_WARNING_ASSERTION(IsControl(),
964 "eContextMenu events with eControlClick trigger "
965 "should return true from IsControl()");
968 #endif
970 /******************************************************************************
971 * mozilla::WidgetDragEvent (MouseEvents.h)
972 ******************************************************************************/
974 void WidgetDragEvent::InitDropEffectForTests() {
975 MOZ_ASSERT(mFlags.mIsSynthesizedForTests);
976 MOZ_ASSERT(mWidget);
978 nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession(mWidget);
979 if (NS_WARN_IF(!session)) {
980 return;
983 uint32_t effectAllowed = session->GetEffectAllowedForTests();
984 uint32_t desiredDropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
985 #ifdef XP_MACOSX
986 if (IsAlt()) {
987 desiredDropEffect = IsMeta() ? nsIDragService::DRAGDROP_ACTION_LINK
988 : nsIDragService::DRAGDROP_ACTION_COPY;
990 #else
991 // On Linux, we know user's intention from API, but we should use
992 // same modifiers as Windows for tests because GNOME on Ubuntu use
993 // them and that makes each test simpler.
994 if (IsControl()) {
995 desiredDropEffect = IsShift() ? nsIDragService::DRAGDROP_ACTION_LINK
996 : nsIDragService::DRAGDROP_ACTION_COPY;
997 } else if (IsShift()) {
998 desiredDropEffect = nsIDragService::DRAGDROP_ACTION_MOVE;
1000 #endif // #ifdef XP_MACOSX #else
1001 // First, use modifier state for preferring action which is explicitly
1002 // specified by the synthesizer.
1003 if (!(desiredDropEffect &= effectAllowed)) {
1004 // Otherwise, use an action which is allowed at starting the session.
1005 desiredDropEffect = effectAllowed;
1007 if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_MOVE) {
1008 session->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
1009 } else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_COPY) {
1010 session->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
1011 } else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_LINK) {
1012 session->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
1013 } else {
1014 session->SetDragAction(nsIDragService::DRAGDROP_ACTION_NONE);
1018 /******************************************************************************
1019 * mozilla::WidgetWheelEvent (MouseEvents.h)
1020 ******************************************************************************/
1022 /* static */
1023 double WidgetWheelEvent::ComputeOverriddenDelta(double aDelta,
1024 bool aIsForVertical) {
1025 if (!StaticPrefs::mousewheel_system_scroll_override_enabled()) {
1026 return aDelta;
1028 int32_t intFactor =
1029 aIsForVertical
1030 ? StaticPrefs::mousewheel_system_scroll_override_vertical_factor()
1031 : StaticPrefs::mousewheel_system_scroll_override_horizontal_factor();
1032 // Making the scroll speed slower doesn't make sense. So, ignore odd factor
1033 // which is less than 1.0.
1034 if (intFactor <= 100) {
1035 return aDelta;
1037 double factor = static_cast<double>(intFactor) / 100;
1038 return aDelta * factor;
1041 double WidgetWheelEvent::OverriddenDeltaX() const {
1042 if (!mAllowToOverrideSystemScrollSpeed ||
1043 mDeltaMode != dom::WheelEvent_Binding::DOM_DELTA_LINE ||
1044 mCustomizedByUserPrefs) {
1045 return mDeltaX;
1047 return ComputeOverriddenDelta(mDeltaX, false);
1050 double WidgetWheelEvent::OverriddenDeltaY() const {
1051 if (!mAllowToOverrideSystemScrollSpeed ||
1052 mDeltaMode != dom::WheelEvent_Binding::DOM_DELTA_LINE ||
1053 mCustomizedByUserPrefs) {
1054 return mDeltaY;
1056 return ComputeOverriddenDelta(mDeltaY, true);
1059 /******************************************************************************
1060 * mozilla::WidgetKeyboardEvent (TextEvents.h)
1061 ******************************************************************************/
1063 #define NS_DEFINE_KEYNAME(aCPPName, aDOMKeyName) (u"" aDOMKeyName),
1064 const char16_t* const WidgetKeyboardEvent::kKeyNames[] = {
1065 #include "mozilla/KeyNameList.h"
1067 #undef NS_DEFINE_KEYNAME
1069 #define NS_DEFINE_PHYSICAL_KEY_CODE_NAME(aCPPName, aDOMCodeName) \
1070 (u"" aDOMCodeName),
1071 const char16_t* const WidgetKeyboardEvent::kCodeNames[] = {
1072 #include "mozilla/PhysicalKeyCodeNameList.h"
1074 #undef NS_DEFINE_PHYSICAL_KEY_CODE_NAME
1076 WidgetKeyboardEvent::KeyNameIndexHashtable*
1077 WidgetKeyboardEvent::sKeyNameIndexHashtable = nullptr;
1078 WidgetKeyboardEvent::CodeNameIndexHashtable*
1079 WidgetKeyboardEvent::sCodeNameIndexHashtable = nullptr;
1081 void WidgetKeyboardEvent::InitAllEditCommands(
1082 const Maybe<WritingMode>& aWritingMode) {
1083 // If this event is synthesized for tests, we don't need to retrieve the
1084 // command via the main process. So, we don't need widget and can trust
1085 // the event.
1086 if (!mFlags.mIsSynthesizedForTests) {
1087 // If the event was created without widget, e.g., created event in chrome
1088 // script, this shouldn't execute native key bindings.
1089 if (NS_WARN_IF(!mWidget)) {
1090 return;
1093 // This event should be trusted event here and we shouldn't expose native
1094 // key binding information to web contents with untrusted events.
1095 if (NS_WARN_IF(!IsTrusted())) {
1096 return;
1099 MOZ_ASSERT(
1100 XRE_IsParentProcess(),
1101 "It's too expensive to retrieve all edit commands from remote process");
1102 MOZ_ASSERT(!AreAllEditCommandsInitialized(),
1103 "Shouldn't be called two or more times");
1106 DebugOnly<bool> okIgnored = InitEditCommandsFor(
1107 NativeKeyBindingsType::SingleLineEditor, aWritingMode);
1108 NS_WARNING_ASSERTION(okIgnored,
1109 "InitEditCommandsFor(NativeKeyBindingsType::"
1110 "SingleLineEditor) failed, but ignored");
1111 okIgnored =
1112 InitEditCommandsFor(NativeKeyBindingsType::MultiLineEditor, aWritingMode);
1113 NS_WARNING_ASSERTION(okIgnored,
1114 "InitEditCommandsFor(NativeKeyBindingsType::"
1115 "MultiLineEditor) failed, but ignored");
1116 okIgnored =
1117 InitEditCommandsFor(NativeKeyBindingsType::RichTextEditor, aWritingMode);
1118 NS_WARNING_ASSERTION(okIgnored,
1119 "InitEditCommandsFor(NativeKeyBindingsType::"
1120 "RichTextEditor) failed, but ignored");
1123 bool WidgetKeyboardEvent::InitEditCommandsFor(
1124 NativeKeyBindingsType aType, const Maybe<WritingMode>& aWritingMode) {
1125 bool& initialized = IsEditCommandsInitializedRef(aType);
1126 if (initialized) {
1127 return true;
1129 nsTArray<CommandInt>& commands = EditCommandsRef(aType);
1131 // If this event is synthesized for tests, we shouldn't access customized
1132 // shortcut settings of the environment. Therefore, we don't need to check
1133 // whether `widget` is set or not. And we can treat synthesized events are
1134 // always trusted.
1135 if (mFlags.mIsSynthesizedForTests) {
1136 MOZ_DIAGNOSTIC_ASSERT(IsTrusted());
1137 #if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
1138 // TODO: We should implement `NativeKeyBindings` for Windows and Android
1139 // too in bug 1301497 for getting rid of the #if.
1140 widget::NativeKeyBindings::GetEditCommandsForTests(aType, *this,
1141 aWritingMode, commands);
1142 #endif
1143 initialized = true;
1144 return true;
1147 if (NS_WARN_IF(!mWidget) || NS_WARN_IF(!IsTrusted())) {
1148 return false;
1150 // `nsIWidget::GetEditCommands()` will retrieve `WritingMode` at selection
1151 // again, but it should be almost zero-cost since `TextEventDispatcher`
1152 // caches the value.
1153 nsCOMPtr<nsIWidget> widget = mWidget;
1154 initialized = widget->GetEditCommands(aType, *this, commands);
1155 return initialized;
1158 bool WidgetKeyboardEvent::ExecuteEditCommands(NativeKeyBindingsType aType,
1159 DoCommandCallback aCallback,
1160 void* aCallbackData) {
1161 // If the event was created without widget, e.g., created event in chrome
1162 // script, this shouldn't execute native key bindings.
1163 if (NS_WARN_IF(!mWidget)) {
1164 return false;
1167 // This event should be trusted event here and we shouldn't expose native
1168 // key binding information to web contents with untrusted events.
1169 if (NS_WARN_IF(!IsTrusted())) {
1170 return false;
1173 if (!IsEditCommandsInitializedRef(aType)) {
1174 Maybe<WritingMode> writingMode;
1175 if (RefPtr<widget::TextEventDispatcher> textEventDispatcher =
1176 mWidget->GetTextEventDispatcher()) {
1177 writingMode = textEventDispatcher->MaybeQueryWritingModeAtSelection();
1179 if (NS_WARN_IF(!InitEditCommandsFor(aType, writingMode))) {
1180 return false;
1184 const nsTArray<CommandInt>& commands = EditCommandsRef(aType);
1185 if (commands.IsEmpty()) {
1186 return false;
1189 for (CommandInt command : commands) {
1190 aCallback(static_cast<Command>(command), aCallbackData);
1192 return true;
1195 bool WidgetKeyboardEvent::ShouldCauseKeypressEvents() const {
1196 // Currently, we don't dispatch keypress events of modifier keys and
1197 // dead keys.
1198 switch (mKeyNameIndex) {
1199 case KEY_NAME_INDEX_Alt:
1200 case KEY_NAME_INDEX_AltGraph:
1201 case KEY_NAME_INDEX_CapsLock:
1202 case KEY_NAME_INDEX_Control:
1203 case KEY_NAME_INDEX_Fn:
1204 case KEY_NAME_INDEX_FnLock:
1205 // case KEY_NAME_INDEX_Hyper:
1206 case KEY_NAME_INDEX_Meta:
1207 case KEY_NAME_INDEX_NumLock:
1208 case KEY_NAME_INDEX_ScrollLock:
1209 case KEY_NAME_INDEX_Shift:
1210 // case KEY_NAME_INDEX_Super:
1211 case KEY_NAME_INDEX_Symbol:
1212 case KEY_NAME_INDEX_SymbolLock:
1213 case KEY_NAME_INDEX_Dead:
1214 return false;
1215 default:
1216 return true;
1220 static bool HasASCIIDigit(const ShortcutKeyCandidateArray& aCandidates) {
1221 for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
1222 uint32_t ch = aCandidates[i].mCharCode;
1223 if (ch >= '0' && ch <= '9') return true;
1225 return false;
1228 static bool CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2) {
1229 return aChar1 == aChar2 || (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
1230 ToLowerCase(static_cast<char16_t>(aChar1)) ==
1231 ToLowerCase(static_cast<char16_t>(aChar2)));
1234 static bool IsCaseChangeableChar(uint32_t aChar) {
1235 return IS_IN_BMP(aChar) && ToLowerCase(static_cast<char16_t>(aChar)) !=
1236 ToUpperCase(static_cast<char16_t>(aChar));
1239 void WidgetKeyboardEvent::GetShortcutKeyCandidates(
1240 ShortcutKeyCandidateArray& aCandidates) const {
1241 MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
1243 using ShiftState = ShortcutKeyCandidate::ShiftState;
1244 using SkipIfEarlierHandlerDisabled =
1245 ShortcutKeyCandidate::SkipIfEarlierHandlerDisabled;
1247 // ShortcutKeyCandidate::mCharCode is a candidate charCode.
1248 // ShortcutKeyCandidate::mShiftState means the mCharCode should be tried to
1249 // execute a command with/without shift key state. If this is Ignorable,
1250 // the shifted key state should be ignored. Otherwise, don't ignore the state.
1251 // the priority of the charCodes are (shift key is not pressed):
1252 // 0: PseudoCharCode()/ShiftState::MatchExactly,
1253 // 1: unshiftedCharCodes[0]/ShiftState::MatchExactly,
1254 // 2: unshiftedCharCodes[1]/ShiftState::MatchExactly...
1255 // the priority of the charCodes are (shift key is pressed):
1256 // 0: PseudoCharCode()/ShiftState::MatchExactly,
1257 // 1: shiftedCharCodes[0]/ShiftState::MatchExactly,
1258 // 2: shiftedCharCodes[0]/ShiftState::Ignorable,
1259 // 3: shiftedCharCodes[1]/ShiftState::MatchExactly,
1260 // 4: shiftedCharCodes[1]/ShiftState::Ignorable...
1261 uint32_t pseudoCharCode = PseudoCharCode();
1262 if (pseudoCharCode) {
1263 ShortcutKeyCandidate key(pseudoCharCode, ShiftState::MatchExactly,
1264 SkipIfEarlierHandlerDisabled::No);
1265 aCandidates.AppendElement(key);
1268 uint32_t len = mAlternativeCharCodes.Length();
1269 if (!IsShift()) {
1270 for (uint32_t i = 0; i < len; ++i) {
1271 uint32_t ch = mAlternativeCharCodes[i].mUnshiftedCharCode;
1272 if (!ch || ch == pseudoCharCode) {
1273 continue;
1275 ShortcutKeyCandidate key(ch, ShiftState::MatchExactly,
1276 SkipIfEarlierHandlerDisabled::No);
1277 aCandidates.AppendElement(key);
1279 // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
1280 // this keyboard layout is AZERTY or similar layout, probably.
1281 // In this case, Accel+[0-9] should be accessible without shift key.
1282 // However, the priority should be lowest.
1283 if (!HasASCIIDigit(aCandidates)) {
1284 for (uint32_t i = 0; i < len; ++i) {
1285 uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode;
1286 if (ch >= '0' && ch <= '9') {
1287 ShortcutKeyCandidate key(
1288 ch, ShiftState::MatchExactly,
1289 // Ctrl + `-` in the French keyboard layout should not match with
1290 // Ctrl + `6` shortcut when it's already fully zoomed out.
1291 SkipIfEarlierHandlerDisabled::Yes);
1292 aCandidates.AppendElement(key);
1293 break;
1297 } else {
1298 for (uint32_t i = 0; i < len; ++i) {
1299 uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode;
1300 if (!ch) {
1301 continue;
1304 if (ch != pseudoCharCode) {
1305 ShortcutKeyCandidate key(ch, ShiftState::MatchExactly,
1306 SkipIfEarlierHandlerDisabled::No);
1307 aCandidates.AppendElement(key);
1310 // If the char is an alphabet, the shift key state should not be
1311 // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
1313 // And checking the charCode is same as unshiftedCharCode too.
1314 // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
1315 uint32_t unshiftCh = mAlternativeCharCodes[i].mUnshiftedCharCode;
1316 if (CharsCaseInsensitiveEqual(ch, unshiftCh)) {
1317 continue;
1320 // On the Hebrew keyboard layout on Windows, the unshifted char is a
1321 // localized character but the shifted char is a Latin alphabet,
1322 // then, we should not execute without the shift state. See bug 433192.
1323 if (IsCaseChangeableChar(ch)) {
1324 continue;
1327 // Setting the alternative charCode candidates for retry without shift
1328 // key state only when the shift key is pressed.
1329 ShortcutKeyCandidate key(ch, ShiftState::Ignorable,
1330 SkipIfEarlierHandlerDisabled::No);
1331 aCandidates.AppendElement(key);
1335 // Special case for "Space" key. With some keyboard layouts, "Space" with
1336 // or without Shift key causes non-ASCII space. For such keyboard layouts,
1337 // we should guarantee that the key press works as an ASCII white space key
1338 // press. However, if the space key is assigned to a function key, it
1339 // shouldn't work as a space key.
1340 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
1341 mCodeNameIndex == CODE_NAME_INDEX_Space && pseudoCharCode != ' ') {
1342 ShortcutKeyCandidate spaceKey(' ', ShiftState::MatchExactly,
1343 SkipIfEarlierHandlerDisabled::No);
1344 aCandidates.AppendElement(spaceKey);
1348 void WidgetKeyboardEvent::GetAccessKeyCandidates(
1349 nsTArray<uint32_t>& aCandidates) const {
1350 MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
1352 // return the lower cased charCode candidates for access keys.
1353 // the priority of the charCodes are:
1354 // 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
1355 // 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
1356 uint32_t pseudoCharCode = PseudoCharCode();
1357 if (pseudoCharCode) {
1358 uint32_t ch = pseudoCharCode;
1359 if (IS_IN_BMP(ch)) {
1360 ch = ToLowerCase(static_cast<char16_t>(ch));
1362 aCandidates.AppendElement(ch);
1364 for (uint32_t i = 0; i < mAlternativeCharCodes.Length(); ++i) {
1365 uint32_t ch[2] = {mAlternativeCharCodes[i].mUnshiftedCharCode,
1366 mAlternativeCharCodes[i].mShiftedCharCode};
1367 for (uint32_t j = 0; j < 2; ++j) {
1368 if (!ch[j]) {
1369 continue;
1371 if (IS_IN_BMP(ch[j])) {
1372 ch[j] = ToLowerCase(static_cast<char16_t>(ch[j]));
1374 // Don't append the charcode that was already appended.
1375 if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex) {
1376 aCandidates.AppendElement(ch[j]);
1380 // Special case for "Space" key. With some keyboard layouts, "Space" with
1381 // or without Shift key causes non-ASCII space. For such keyboard layouts,
1382 // we should guarantee that the key press works as an ASCII white space key
1383 // press. However, if the space key is assigned to a function key, it
1384 // shouldn't work as a space key.
1385 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
1386 mCodeNameIndex == CODE_NAME_INDEX_Space && pseudoCharCode != ' ') {
1387 aCandidates.AppendElement(' ');
1391 // mask values for ui.key.chromeAccess and ui.key.contentAccess
1392 #define NS_MODIFIER_SHIFT 1
1393 #define NS_MODIFIER_CONTROL 2
1394 #define NS_MODIFIER_ALT 4
1395 #define NS_MODIFIER_META 8
1397 static Modifiers PrefFlagsToModifiers(int32_t aPrefFlags) {
1398 Modifiers result = 0;
1399 if (aPrefFlags & NS_MODIFIER_SHIFT) {
1400 result |= MODIFIER_SHIFT;
1402 if (aPrefFlags & NS_MODIFIER_CONTROL) {
1403 result |= MODIFIER_CONTROL;
1405 if (aPrefFlags & NS_MODIFIER_ALT) {
1406 result |= MODIFIER_ALT;
1408 if (aPrefFlags & NS_MODIFIER_META) {
1409 result |= MODIFIER_META;
1411 return result;
1414 bool WidgetKeyboardEvent::ModifiersMatchWithAccessKey(
1415 AccessKeyType aType) const {
1416 if (!ModifiersForAccessKeyMatching()) {
1417 return false;
1419 return ModifiersForAccessKeyMatching() == AccessKeyModifiers(aType);
1422 Modifiers WidgetKeyboardEvent::ModifiersForAccessKeyMatching() const {
1423 static const Modifiers kModifierMask =
1424 MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_META;
1425 return mModifiers & kModifierMask;
1428 /* static */
1429 Modifiers WidgetKeyboardEvent::AccessKeyModifiers(AccessKeyType aType) {
1430 switch (StaticPrefs::ui_key_generalAccessKey()) {
1431 case -1:
1432 break; // use the individual prefs
1433 case NS_VK_SHIFT:
1434 return MODIFIER_SHIFT;
1435 case NS_VK_CONTROL:
1436 return MODIFIER_CONTROL;
1437 case NS_VK_ALT:
1438 return MODIFIER_ALT;
1439 case NS_VK_META:
1440 case NS_VK_WIN:
1441 return MODIFIER_META;
1442 default:
1443 return MODIFIER_NONE;
1446 switch (aType) {
1447 case AccessKeyType::eChrome:
1448 return PrefFlagsToModifiers(StaticPrefs::ui_key_chromeAccess());
1449 case AccessKeyType::eContent:
1450 return PrefFlagsToModifiers(StaticPrefs::ui_key_contentAccess());
1451 default:
1452 return MODIFIER_NONE;
1456 /* static */
1457 void WidgetKeyboardEvent::Shutdown() {
1458 delete sKeyNameIndexHashtable;
1459 sKeyNameIndexHashtable = nullptr;
1460 delete sCodeNameIndexHashtable;
1461 sCodeNameIndexHashtable = nullptr;
1462 // Although sCommandHashtable is not a member of WidgetKeyboardEvent, but
1463 // let's delete it here since we need to do it at same time.
1464 delete sCommandHashtable;
1465 sCommandHashtable = nullptr;
1468 /* static */
1469 void WidgetKeyboardEvent::GetDOMKeyName(KeyNameIndex aKeyNameIndex,
1470 nsAString& aKeyName) {
1471 if (aKeyNameIndex >= KEY_NAME_INDEX_USE_STRING) {
1472 aKeyName.Truncate();
1473 return;
1476 MOZ_RELEASE_ASSERT(static_cast<size_t>(aKeyNameIndex) < std::size(kKeyNames),
1477 "Illegal key enumeration value");
1478 aKeyName = kKeyNames[aKeyNameIndex];
1481 /* static */
1482 void WidgetKeyboardEvent::GetDOMCodeName(CodeNameIndex aCodeNameIndex,
1483 nsAString& aCodeName) {
1484 if (aCodeNameIndex >= CODE_NAME_INDEX_USE_STRING) {
1485 aCodeName.Truncate();
1486 return;
1489 MOZ_RELEASE_ASSERT(
1490 static_cast<size_t>(aCodeNameIndex) < std::size(kCodeNames),
1491 "Illegal physical code enumeration value");
1493 // Generate some continuous runs of codes, rather than looking them up.
1494 if (aCodeNameIndex >= CODE_NAME_INDEX_KeyA &&
1495 aCodeNameIndex <= CODE_NAME_INDEX_KeyZ) {
1496 uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_KeyA;
1497 aCodeName.AssignLiteral(u"Key");
1498 aCodeName.Append(u'A' + index);
1499 return;
1501 if (aCodeNameIndex >= CODE_NAME_INDEX_Digit0 &&
1502 aCodeNameIndex <= CODE_NAME_INDEX_Digit9) {
1503 uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_Digit0;
1504 aCodeName.AssignLiteral(u"Digit");
1505 aCodeName.AppendInt(index);
1506 return;
1508 if (aCodeNameIndex >= CODE_NAME_INDEX_Numpad0 &&
1509 aCodeNameIndex <= CODE_NAME_INDEX_Numpad9) {
1510 uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_Numpad0;
1511 aCodeName.AssignLiteral(u"Numpad");
1512 aCodeName.AppendInt(index);
1513 return;
1515 if (aCodeNameIndex >= CODE_NAME_INDEX_F1 &&
1516 aCodeNameIndex <= CODE_NAME_INDEX_F24) {
1517 uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_F1;
1518 aCodeName.Assign(u'F');
1519 aCodeName.AppendInt(index + 1);
1520 return;
1523 aCodeName = kCodeNames[aCodeNameIndex];
1526 /* static */
1527 KeyNameIndex WidgetKeyboardEvent::GetKeyNameIndex(const nsAString& aKeyValue) {
1528 if (!sKeyNameIndexHashtable) {
1529 sKeyNameIndexHashtable = new KeyNameIndexHashtable(std::size(kKeyNames));
1530 for (size_t i = 0; i < std::size(kKeyNames); i++) {
1531 sKeyNameIndexHashtable->InsertOrUpdate(nsDependentString(kKeyNames[i]),
1532 static_cast<KeyNameIndex>(i));
1535 return sKeyNameIndexHashtable->MaybeGet(aKeyValue).valueOr(
1536 KEY_NAME_INDEX_USE_STRING);
1539 /* static */
1540 CodeNameIndex WidgetKeyboardEvent::GetCodeNameIndex(
1541 const nsAString& aCodeValue) {
1542 if (!sCodeNameIndexHashtable) {
1543 sCodeNameIndexHashtable = new CodeNameIndexHashtable(std::size(kCodeNames));
1544 for (size_t i = 0; i < std::size(kCodeNames); i++) {
1545 sCodeNameIndexHashtable->InsertOrUpdate(nsDependentString(kCodeNames[i]),
1546 static_cast<CodeNameIndex>(i));
1549 return sCodeNameIndexHashtable->MaybeGet(aCodeValue)
1550 .valueOr(CODE_NAME_INDEX_USE_STRING);
1553 /* static */
1554 uint32_t WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(
1555 CodeNameIndex aCodeNameIndex) {
1556 switch (aCodeNameIndex) {
1557 case CODE_NAME_INDEX_Semicolon: // VK_OEM_1 on Windows
1558 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON;
1559 case CODE_NAME_INDEX_Equal: // VK_OEM_PLUS on Windows
1560 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS;
1561 case CODE_NAME_INDEX_Comma: // VK_OEM_COMMA on Windows
1562 return dom::KeyboardEvent_Binding::DOM_VK_COMMA;
1563 case CODE_NAME_INDEX_Minus: // VK_OEM_MINUS on Windows
1564 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS;
1565 case CODE_NAME_INDEX_Period: // VK_OEM_PERIOD on Windows
1566 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD;
1567 case CODE_NAME_INDEX_Slash: // VK_OEM_2 on Windows
1568 return dom::KeyboardEvent_Binding::DOM_VK_SLASH;
1569 case CODE_NAME_INDEX_Backquote: // VK_OEM_3 on Windows
1570 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE;
1571 case CODE_NAME_INDEX_BracketLeft: // VK_OEM_4 on Windows
1572 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET;
1573 case CODE_NAME_INDEX_Backslash: // VK_OEM_5 on Windows
1574 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
1575 case CODE_NAME_INDEX_BracketRight: // VK_OEM_6 on Windows
1576 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET;
1577 case CODE_NAME_INDEX_Quote: // VK_OEM_7 on Windows
1578 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE;
1579 case CODE_NAME_INDEX_IntlBackslash: // VK_OEM_5 on Windows (ABNT, etc)
1580 case CODE_NAME_INDEX_IntlYen: // VK_OEM_5 on Windows (JIS)
1581 case CODE_NAME_INDEX_IntlRo: // VK_OEM_102 on Windows
1582 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
1583 default:
1584 return 0;
1588 /* static */ const char* WidgetKeyboardEvent::GetCommandStr(Command aCommand) {
1589 #define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr
1590 #define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam) , #aCommandStr
1591 #define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) , ""
1592 static const char* const kCommands[] = {
1593 "" // DoNothing
1594 #include "mozilla/CommandList.h"
1596 #undef NS_DEFINE_COMMAND
1597 #undef NS_DEFINE_COMMAND_WITH_PARAM
1598 #undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND
1600 MOZ_RELEASE_ASSERT(static_cast<size_t>(aCommand) < std::size(kCommands),
1601 "Illegal command enumeration value");
1602 return kCommands[static_cast<CommandInt>(aCommand)];
1605 /* static */
1606 uint32_t WidgetKeyboardEvent::ComputeLocationFromCodeValue(
1607 CodeNameIndex aCodeNameIndex) {
1608 // Following commented out cases are not defined in PhysicalKeyCodeNameList.h
1609 // but are defined by D3E spec. So, they should be uncommented when the
1610 // code values are defined in the header.
1611 switch (aCodeNameIndex) {
1612 case CODE_NAME_INDEX_AltLeft:
1613 case CODE_NAME_INDEX_ControlLeft:
1614 case CODE_NAME_INDEX_MetaLeft:
1615 case CODE_NAME_INDEX_ShiftLeft:
1616 return eKeyLocationLeft;
1617 case CODE_NAME_INDEX_AltRight:
1618 case CODE_NAME_INDEX_ControlRight:
1619 case CODE_NAME_INDEX_MetaRight:
1620 case CODE_NAME_INDEX_ShiftRight:
1621 return eKeyLocationRight;
1622 case CODE_NAME_INDEX_Numpad0:
1623 case CODE_NAME_INDEX_Numpad1:
1624 case CODE_NAME_INDEX_Numpad2:
1625 case CODE_NAME_INDEX_Numpad3:
1626 case CODE_NAME_INDEX_Numpad4:
1627 case CODE_NAME_INDEX_Numpad5:
1628 case CODE_NAME_INDEX_Numpad6:
1629 case CODE_NAME_INDEX_Numpad7:
1630 case CODE_NAME_INDEX_Numpad8:
1631 case CODE_NAME_INDEX_Numpad9:
1632 case CODE_NAME_INDEX_NumpadAdd:
1633 case CODE_NAME_INDEX_NumpadBackspace:
1634 case CODE_NAME_INDEX_NumpadClear:
1635 case CODE_NAME_INDEX_NumpadClearEntry:
1636 case CODE_NAME_INDEX_NumpadComma:
1637 case CODE_NAME_INDEX_NumpadDecimal:
1638 case CODE_NAME_INDEX_NumpadDivide:
1639 case CODE_NAME_INDEX_NumpadEnter:
1640 case CODE_NAME_INDEX_NumpadEqual:
1641 case CODE_NAME_INDEX_NumpadMemoryAdd:
1642 case CODE_NAME_INDEX_NumpadMemoryClear:
1643 case CODE_NAME_INDEX_NumpadMemoryRecall:
1644 case CODE_NAME_INDEX_NumpadMemoryStore:
1645 case CODE_NAME_INDEX_NumpadMemorySubtract:
1646 case CODE_NAME_INDEX_NumpadMultiply:
1647 case CODE_NAME_INDEX_NumpadParenLeft:
1648 case CODE_NAME_INDEX_NumpadParenRight:
1649 case CODE_NAME_INDEX_NumpadSubtract:
1650 return eKeyLocationNumpad;
1651 default:
1652 return eKeyLocationStandard;
1656 /* static */
1657 uint32_t WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
1658 KeyNameIndex aKeyNameIndex) {
1659 switch (aKeyNameIndex) {
1660 case KEY_NAME_INDEX_Cancel:
1661 return dom::KeyboardEvent_Binding::DOM_VK_CANCEL;
1662 case KEY_NAME_INDEX_Help:
1663 return dom::KeyboardEvent_Binding::DOM_VK_HELP;
1664 case KEY_NAME_INDEX_Backspace:
1665 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SPACE;
1666 case KEY_NAME_INDEX_Tab:
1667 return dom::KeyboardEvent_Binding::DOM_VK_TAB;
1668 case KEY_NAME_INDEX_Clear:
1669 return dom::KeyboardEvent_Binding::DOM_VK_CLEAR;
1670 case KEY_NAME_INDEX_Enter:
1671 return dom::KeyboardEvent_Binding::DOM_VK_RETURN;
1672 case KEY_NAME_INDEX_Shift:
1673 return dom::KeyboardEvent_Binding::DOM_VK_SHIFT;
1674 case KEY_NAME_INDEX_Control:
1675 return dom::KeyboardEvent_Binding::DOM_VK_CONTROL;
1676 case KEY_NAME_INDEX_Alt:
1677 return dom::KeyboardEvent_Binding::DOM_VK_ALT;
1678 case KEY_NAME_INDEX_Pause:
1679 return dom::KeyboardEvent_Binding::DOM_VK_PAUSE;
1680 case KEY_NAME_INDEX_CapsLock:
1681 return dom::KeyboardEvent_Binding::DOM_VK_CAPS_LOCK;
1682 case KEY_NAME_INDEX_Hiragana:
1683 case KEY_NAME_INDEX_Katakana:
1684 case KEY_NAME_INDEX_HiraganaKatakana:
1685 case KEY_NAME_INDEX_KanaMode:
1686 return dom::KeyboardEvent_Binding::DOM_VK_KANA;
1687 case KEY_NAME_INDEX_HangulMode:
1688 return dom::KeyboardEvent_Binding::DOM_VK_HANGUL;
1689 case KEY_NAME_INDEX_Eisu:
1690 return dom::KeyboardEvent_Binding::DOM_VK_EISU;
1691 case KEY_NAME_INDEX_JunjaMode:
1692 return dom::KeyboardEvent_Binding::DOM_VK_JUNJA;
1693 case KEY_NAME_INDEX_FinalMode:
1694 return dom::KeyboardEvent_Binding::DOM_VK_FINAL;
1695 case KEY_NAME_INDEX_HanjaMode:
1696 return dom::KeyboardEvent_Binding::DOM_VK_HANJA;
1697 case KEY_NAME_INDEX_KanjiMode:
1698 return dom::KeyboardEvent_Binding::DOM_VK_KANJI;
1699 case KEY_NAME_INDEX_Escape:
1700 return dom::KeyboardEvent_Binding::DOM_VK_ESCAPE;
1701 case KEY_NAME_INDEX_Convert:
1702 return dom::KeyboardEvent_Binding::DOM_VK_CONVERT;
1703 case KEY_NAME_INDEX_NonConvert:
1704 return dom::KeyboardEvent_Binding::DOM_VK_NONCONVERT;
1705 case KEY_NAME_INDEX_Accept:
1706 return dom::KeyboardEvent_Binding::DOM_VK_ACCEPT;
1707 case KEY_NAME_INDEX_ModeChange:
1708 return dom::KeyboardEvent_Binding::DOM_VK_MODECHANGE;
1709 case KEY_NAME_INDEX_PageUp:
1710 return dom::KeyboardEvent_Binding::DOM_VK_PAGE_UP;
1711 case KEY_NAME_INDEX_PageDown:
1712 return dom::KeyboardEvent_Binding::DOM_VK_PAGE_DOWN;
1713 case KEY_NAME_INDEX_End:
1714 return dom::KeyboardEvent_Binding::DOM_VK_END;
1715 case KEY_NAME_INDEX_Home:
1716 return dom::KeyboardEvent_Binding::DOM_VK_HOME;
1717 case KEY_NAME_INDEX_ArrowLeft:
1718 return dom::KeyboardEvent_Binding::DOM_VK_LEFT;
1719 case KEY_NAME_INDEX_ArrowUp:
1720 return dom::KeyboardEvent_Binding::DOM_VK_UP;
1721 case KEY_NAME_INDEX_ArrowRight:
1722 return dom::KeyboardEvent_Binding::DOM_VK_RIGHT;
1723 case KEY_NAME_INDEX_ArrowDown:
1724 return dom::KeyboardEvent_Binding::DOM_VK_DOWN;
1725 case KEY_NAME_INDEX_Select:
1726 return dom::KeyboardEvent_Binding::DOM_VK_SELECT;
1727 case KEY_NAME_INDEX_Print:
1728 return dom::KeyboardEvent_Binding::DOM_VK_PRINT;
1729 case KEY_NAME_INDEX_Execute:
1730 return dom::KeyboardEvent_Binding::DOM_VK_EXECUTE;
1731 case KEY_NAME_INDEX_PrintScreen:
1732 return dom::KeyboardEvent_Binding::DOM_VK_PRINTSCREEN;
1733 case KEY_NAME_INDEX_Insert:
1734 return dom::KeyboardEvent_Binding::DOM_VK_INSERT;
1735 case KEY_NAME_INDEX_Delete:
1736 return dom::KeyboardEvent_Binding::DOM_VK_DELETE;
1737 case KEY_NAME_INDEX_ContextMenu:
1738 return dom::KeyboardEvent_Binding::DOM_VK_CONTEXT_MENU;
1739 case KEY_NAME_INDEX_Standby:
1740 return dom::KeyboardEvent_Binding::DOM_VK_SLEEP;
1741 case KEY_NAME_INDEX_F1:
1742 return dom::KeyboardEvent_Binding::DOM_VK_F1;
1743 case KEY_NAME_INDEX_F2:
1744 return dom::KeyboardEvent_Binding::DOM_VK_F2;
1745 case KEY_NAME_INDEX_F3:
1746 return dom::KeyboardEvent_Binding::DOM_VK_F3;
1747 case KEY_NAME_INDEX_F4:
1748 return dom::KeyboardEvent_Binding::DOM_VK_F4;
1749 case KEY_NAME_INDEX_F5:
1750 return dom::KeyboardEvent_Binding::DOM_VK_F5;
1751 case KEY_NAME_INDEX_F6:
1752 return dom::KeyboardEvent_Binding::DOM_VK_F6;
1753 case KEY_NAME_INDEX_F7:
1754 return dom::KeyboardEvent_Binding::DOM_VK_F7;
1755 case KEY_NAME_INDEX_F8:
1756 return dom::KeyboardEvent_Binding::DOM_VK_F8;
1757 case KEY_NAME_INDEX_F9:
1758 return dom::KeyboardEvent_Binding::DOM_VK_F9;
1759 case KEY_NAME_INDEX_F10:
1760 return dom::KeyboardEvent_Binding::DOM_VK_F10;
1761 case KEY_NAME_INDEX_F11:
1762 return dom::KeyboardEvent_Binding::DOM_VK_F11;
1763 case KEY_NAME_INDEX_F12:
1764 return dom::KeyboardEvent_Binding::DOM_VK_F12;
1765 case KEY_NAME_INDEX_F13:
1766 return dom::KeyboardEvent_Binding::DOM_VK_F13;
1767 case KEY_NAME_INDEX_F14:
1768 return dom::KeyboardEvent_Binding::DOM_VK_F14;
1769 case KEY_NAME_INDEX_F15:
1770 return dom::KeyboardEvent_Binding::DOM_VK_F15;
1771 case KEY_NAME_INDEX_F16:
1772 return dom::KeyboardEvent_Binding::DOM_VK_F16;
1773 case KEY_NAME_INDEX_F17:
1774 return dom::KeyboardEvent_Binding::DOM_VK_F17;
1775 case KEY_NAME_INDEX_F18:
1776 return dom::KeyboardEvent_Binding::DOM_VK_F18;
1777 case KEY_NAME_INDEX_F19:
1778 return dom::KeyboardEvent_Binding::DOM_VK_F19;
1779 case KEY_NAME_INDEX_F20:
1780 return dom::KeyboardEvent_Binding::DOM_VK_F20;
1781 case KEY_NAME_INDEX_F21:
1782 return dom::KeyboardEvent_Binding::DOM_VK_F21;
1783 case KEY_NAME_INDEX_F22:
1784 return dom::KeyboardEvent_Binding::DOM_VK_F22;
1785 case KEY_NAME_INDEX_F23:
1786 return dom::KeyboardEvent_Binding::DOM_VK_F23;
1787 case KEY_NAME_INDEX_F24:
1788 return dom::KeyboardEvent_Binding::DOM_VK_F24;
1789 case KEY_NAME_INDEX_NumLock:
1790 return dom::KeyboardEvent_Binding::DOM_VK_NUM_LOCK;
1791 case KEY_NAME_INDEX_ScrollLock:
1792 return dom::KeyboardEvent_Binding::DOM_VK_SCROLL_LOCK;
1793 case KEY_NAME_INDEX_AudioVolumeMute:
1794 return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_MUTE;
1795 case KEY_NAME_INDEX_AudioVolumeDown:
1796 return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_DOWN;
1797 case KEY_NAME_INDEX_AudioVolumeUp:
1798 return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_UP;
1799 case KEY_NAME_INDEX_Meta:
1800 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
1801 return dom::KeyboardEvent_Binding::DOM_VK_WIN;
1802 #else
1803 return dom::KeyboardEvent_Binding::DOM_VK_META;
1804 #endif
1805 case KEY_NAME_INDEX_AltGraph:
1806 return dom::KeyboardEvent_Binding::DOM_VK_ALTGR;
1807 case KEY_NAME_INDEX_Process:
1808 return dom::KeyboardEvent_Binding::DOM_VK_PROCESSKEY;
1809 case KEY_NAME_INDEX_Attn:
1810 return dom::KeyboardEvent_Binding::DOM_VK_ATTN;
1811 case KEY_NAME_INDEX_CrSel:
1812 return dom::KeyboardEvent_Binding::DOM_VK_CRSEL;
1813 case KEY_NAME_INDEX_ExSel:
1814 return dom::KeyboardEvent_Binding::DOM_VK_EXSEL;
1815 case KEY_NAME_INDEX_EraseEof:
1816 return dom::KeyboardEvent_Binding::DOM_VK_EREOF;
1817 case KEY_NAME_INDEX_Play:
1818 return dom::KeyboardEvent_Binding::DOM_VK_PLAY;
1819 case KEY_NAME_INDEX_ZoomToggle:
1820 case KEY_NAME_INDEX_ZoomIn:
1821 case KEY_NAME_INDEX_ZoomOut:
1822 return dom::KeyboardEvent_Binding::DOM_VK_ZOOM;
1823 default:
1824 return 0;
1828 /* static */
1829 CodeNameIndex WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(
1830 KeyNameIndex aKeyNameIndex, const Maybe<uint32_t>& aLocation) {
1831 if (aLocation.isSome() &&
1832 aLocation.value() ==
1833 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
1834 // On macOS, NumLock is not supported. Therefore, this handles
1835 // control key values except "Enter" only on non-macOS platforms.
1836 switch (aKeyNameIndex) {
1837 #ifndef XP_MACOSX
1838 case KEY_NAME_INDEX_Insert:
1839 return CODE_NAME_INDEX_Numpad0;
1840 case KEY_NAME_INDEX_End:
1841 return CODE_NAME_INDEX_Numpad1;
1842 case KEY_NAME_INDEX_ArrowDown:
1843 return CODE_NAME_INDEX_Numpad2;
1844 case KEY_NAME_INDEX_PageDown:
1845 return CODE_NAME_INDEX_Numpad3;
1846 case KEY_NAME_INDEX_ArrowLeft:
1847 return CODE_NAME_INDEX_Numpad4;
1848 case KEY_NAME_INDEX_Clear:
1849 // FYI: "Clear" on macOS should be DOM_KEY_LOCATION_STANDARD.
1850 return CODE_NAME_INDEX_Numpad5;
1851 case KEY_NAME_INDEX_ArrowRight:
1852 return CODE_NAME_INDEX_Numpad6;
1853 case KEY_NAME_INDEX_Home:
1854 return CODE_NAME_INDEX_Numpad7;
1855 case KEY_NAME_INDEX_ArrowUp:
1856 return CODE_NAME_INDEX_Numpad8;
1857 case KEY_NAME_INDEX_PageUp:
1858 return CODE_NAME_INDEX_Numpad9;
1859 case KEY_NAME_INDEX_Delete:
1860 return CODE_NAME_INDEX_NumpadDecimal;
1861 #endif // #ifndef XP_MACOSX
1862 case KEY_NAME_INDEX_Enter:
1863 return CODE_NAME_INDEX_NumpadEnter;
1864 default:
1865 return CODE_NAME_INDEX_UNKNOWN;
1869 if (WidgetKeyboardEvent::IsLeftOrRightModiferKeyNameIndex(aKeyNameIndex)) {
1870 if (aLocation.isSome() &&
1871 (aLocation.value() !=
1872 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_LEFT &&
1873 aLocation.value() !=
1874 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT)) {
1875 return CODE_NAME_INDEX_UNKNOWN;
1877 bool isRight =
1878 aLocation.isSome() &&
1879 aLocation.value() == dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT;
1880 switch (aKeyNameIndex) {
1881 case KEY_NAME_INDEX_Alt:
1882 return isRight ? CODE_NAME_INDEX_AltRight : CODE_NAME_INDEX_AltLeft;
1883 case KEY_NAME_INDEX_Control:
1884 return isRight ? CODE_NAME_INDEX_ControlRight
1885 : CODE_NAME_INDEX_ControlLeft;
1886 case KEY_NAME_INDEX_Shift:
1887 return isRight ? CODE_NAME_INDEX_ShiftRight : CODE_NAME_INDEX_ShiftLeft;
1888 case KEY_NAME_INDEX_Meta:
1889 return isRight ? CODE_NAME_INDEX_MetaRight : CODE_NAME_INDEX_MetaLeft;
1890 default:
1891 return CODE_NAME_INDEX_UNKNOWN;
1895 if (aLocation.isSome() &&
1896 aLocation.value() !=
1897 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
1898 return CODE_NAME_INDEX_UNKNOWN;
1901 switch (aKeyNameIndex) {
1902 // Standard section:
1903 case KEY_NAME_INDEX_Escape:
1904 return CODE_NAME_INDEX_Escape;
1905 case KEY_NAME_INDEX_Tab:
1906 return CODE_NAME_INDEX_Tab;
1907 case KEY_NAME_INDEX_CapsLock:
1908 return CODE_NAME_INDEX_CapsLock;
1909 case KEY_NAME_INDEX_ContextMenu:
1910 return CODE_NAME_INDEX_ContextMenu;
1911 case KEY_NAME_INDEX_Backspace:
1912 return CODE_NAME_INDEX_Backspace;
1913 case KEY_NAME_INDEX_Enter:
1914 return CODE_NAME_INDEX_Enter;
1915 #ifdef XP_MACOSX
1916 // Although, macOS does not fire native key event of "Fn" key, we support
1917 // Fn key event if it's sent by other apps directly.
1918 case KEY_NAME_INDEX_Fn:
1919 return CODE_NAME_INDEX_Fn;
1920 #endif // #ifdef
1922 // Arrow Pad section:
1923 case KEY_NAME_INDEX_ArrowLeft:
1924 return CODE_NAME_INDEX_ArrowLeft;
1925 case KEY_NAME_INDEX_ArrowUp:
1926 return CODE_NAME_INDEX_ArrowUp;
1927 case KEY_NAME_INDEX_ArrowDown:
1928 return CODE_NAME_INDEX_ArrowDown;
1929 case KEY_NAME_INDEX_ArrowRight:
1930 return CODE_NAME_INDEX_ArrowRight;
1932 // Control Pad section:
1933 #ifndef XP_MACOSX
1934 case KEY_NAME_INDEX_Insert:
1935 return CODE_NAME_INDEX_Insert;
1936 #else
1937 case KEY_NAME_INDEX_Help:
1938 return CODE_NAME_INDEX_Help;
1939 #endif // #ifndef XP_MACOSX #else
1940 case KEY_NAME_INDEX_Delete:
1941 return CODE_NAME_INDEX_Delete;
1942 case KEY_NAME_INDEX_Home:
1943 return CODE_NAME_INDEX_Home;
1944 case KEY_NAME_INDEX_End:
1945 return CODE_NAME_INDEX_End;
1946 case KEY_NAME_INDEX_PageUp:
1947 return CODE_NAME_INDEX_PageUp;
1948 case KEY_NAME_INDEX_PageDown:
1949 return CODE_NAME_INDEX_PageDown;
1951 // Function keys:
1952 case KEY_NAME_INDEX_F1:
1953 return CODE_NAME_INDEX_F1;
1954 case KEY_NAME_INDEX_F2:
1955 return CODE_NAME_INDEX_F2;
1956 case KEY_NAME_INDEX_F3:
1957 return CODE_NAME_INDEX_F3;
1958 case KEY_NAME_INDEX_F4:
1959 return CODE_NAME_INDEX_F4;
1960 case KEY_NAME_INDEX_F5:
1961 return CODE_NAME_INDEX_F5;
1962 case KEY_NAME_INDEX_F6:
1963 return CODE_NAME_INDEX_F6;
1964 case KEY_NAME_INDEX_F7:
1965 return CODE_NAME_INDEX_F7;
1966 case KEY_NAME_INDEX_F8:
1967 return CODE_NAME_INDEX_F8;
1968 case KEY_NAME_INDEX_F9:
1969 return CODE_NAME_INDEX_F9;
1970 case KEY_NAME_INDEX_F10:
1971 return CODE_NAME_INDEX_F10;
1972 case KEY_NAME_INDEX_F11:
1973 return CODE_NAME_INDEX_F11;
1974 case KEY_NAME_INDEX_F12:
1975 return CODE_NAME_INDEX_F12;
1976 case KEY_NAME_INDEX_F13:
1977 return CODE_NAME_INDEX_F13;
1978 case KEY_NAME_INDEX_F14:
1979 return CODE_NAME_INDEX_F14;
1980 case KEY_NAME_INDEX_F15:
1981 return CODE_NAME_INDEX_F15;
1982 case KEY_NAME_INDEX_F16:
1983 return CODE_NAME_INDEX_F16;
1984 case KEY_NAME_INDEX_F17:
1985 return CODE_NAME_INDEX_F17;
1986 case KEY_NAME_INDEX_F18:
1987 return CODE_NAME_INDEX_F18;
1988 case KEY_NAME_INDEX_F19:
1989 return CODE_NAME_INDEX_F19;
1990 case KEY_NAME_INDEX_F20:
1991 return CODE_NAME_INDEX_F20;
1992 #ifndef XP_MACOSX
1993 case KEY_NAME_INDEX_F21:
1994 return CODE_NAME_INDEX_F21;
1995 case KEY_NAME_INDEX_F22:
1996 return CODE_NAME_INDEX_F22;
1997 case KEY_NAME_INDEX_F23:
1998 return CODE_NAME_INDEX_F23;
1999 case KEY_NAME_INDEX_F24:
2000 return CODE_NAME_INDEX_F24;
2001 case KEY_NAME_INDEX_Pause:
2002 return CODE_NAME_INDEX_Pause;
2003 case KEY_NAME_INDEX_PrintScreen:
2004 return CODE_NAME_INDEX_PrintScreen;
2005 case KEY_NAME_INDEX_ScrollLock:
2006 return CODE_NAME_INDEX_ScrollLock;
2007 #endif // #ifndef XP_MACOSX
2009 // NumLock key:
2010 #ifndef XP_MACOSX
2011 case KEY_NAME_INDEX_NumLock:
2012 return CODE_NAME_INDEX_NumLock;
2013 #else
2014 case KEY_NAME_INDEX_Clear:
2015 return CODE_NAME_INDEX_NumLock;
2016 #endif // #ifndef XP_MACOSX #else
2018 // Media keys:
2019 case KEY_NAME_INDEX_AudioVolumeDown:
2020 return CODE_NAME_INDEX_VolumeDown;
2021 case KEY_NAME_INDEX_AudioVolumeMute:
2022 return CODE_NAME_INDEX_VolumeMute;
2023 case KEY_NAME_INDEX_AudioVolumeUp:
2024 return CODE_NAME_INDEX_VolumeUp;
2025 #ifndef XP_MACOSX
2026 case KEY_NAME_INDEX_BrowserBack:
2027 return CODE_NAME_INDEX_BrowserBack;
2028 case KEY_NAME_INDEX_BrowserFavorites:
2029 return CODE_NAME_INDEX_BrowserFavorites;
2030 case KEY_NAME_INDEX_BrowserForward:
2031 return CODE_NAME_INDEX_BrowserForward;
2032 case KEY_NAME_INDEX_BrowserRefresh:
2033 return CODE_NAME_INDEX_BrowserRefresh;
2034 case KEY_NAME_INDEX_BrowserSearch:
2035 return CODE_NAME_INDEX_BrowserSearch;
2036 case KEY_NAME_INDEX_BrowserStop:
2037 return CODE_NAME_INDEX_BrowserStop;
2038 case KEY_NAME_INDEX_MediaPlayPause:
2039 return CODE_NAME_INDEX_MediaPlayPause;
2040 case KEY_NAME_INDEX_MediaStop:
2041 return CODE_NAME_INDEX_MediaStop;
2042 case KEY_NAME_INDEX_MediaTrackNext:
2043 return CODE_NAME_INDEX_MediaTrackNext;
2044 case KEY_NAME_INDEX_MediaTrackPrevious:
2045 return CODE_NAME_INDEX_MediaTrackPrevious;
2046 case KEY_NAME_INDEX_LaunchApplication1:
2047 return CODE_NAME_INDEX_LaunchApp1;
2048 #endif // #ifndef XP_MACOSX
2050 // Only Windows and GTK supports the following multimedia keys.
2051 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
2052 case KEY_NAME_INDEX_BrowserHome:
2053 return CODE_NAME_INDEX_BrowserHome;
2054 case KEY_NAME_INDEX_LaunchApplication2:
2055 return CODE_NAME_INDEX_LaunchApp2;
2056 #endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
2058 // Only GTK and Android supports the following multimedia keys.
2059 #if defined(MOZ_WIDGET_GTK) || defined(ANDROID)
2060 case KEY_NAME_INDEX_Eject:
2061 return CODE_NAME_INDEX_Eject;
2062 case KEY_NAME_INDEX_WakeUp:
2063 return CODE_NAME_INDEX_WakeUp;
2064 #endif // #if defined(MOZ_WIDGET_GTK) || defined(ANDROID)
2066 // Only Windows does not support Help key (and macOS handled above).
2067 #if !defined(XP_WIN) && !defined(XP_MACOSX)
2068 case KEY_NAME_INDEX_Help:
2069 return CODE_NAME_INDEX_Help;
2070 #endif // #if !defined(XP_WIN) && !defined(XP_MACOSX)
2072 // IME specific keys:
2073 #ifdef XP_WIN
2074 case KEY_NAME_INDEX_Convert:
2075 return CODE_NAME_INDEX_Convert;
2076 case KEY_NAME_INDEX_NonConvert:
2077 return CODE_NAME_INDEX_NonConvert;
2078 case KEY_NAME_INDEX_Alphanumeric:
2079 return CODE_NAME_INDEX_CapsLock;
2080 case KEY_NAME_INDEX_KanaMode:
2081 case KEY_NAME_INDEX_Romaji:
2082 case KEY_NAME_INDEX_Katakana:
2083 case KEY_NAME_INDEX_Hiragana:
2084 return CODE_NAME_INDEX_KanaMode;
2085 case KEY_NAME_INDEX_Hankaku:
2086 case KEY_NAME_INDEX_Zenkaku:
2087 case KEY_NAME_INDEX_KanjiMode:
2088 return CODE_NAME_INDEX_Backquote;
2089 case KEY_NAME_INDEX_HanjaMode:
2090 return CODE_NAME_INDEX_Lang2;
2091 case KEY_NAME_INDEX_HangulMode:
2092 return CODE_NAME_INDEX_Lang1;
2093 #endif // #ifdef XP_WIN
2095 #ifdef MOZ_WIDGET_GTK
2096 case KEY_NAME_INDEX_Convert:
2097 return CODE_NAME_INDEX_Convert;
2098 case KEY_NAME_INDEX_NonConvert:
2099 return CODE_NAME_INDEX_NonConvert;
2100 case KEY_NAME_INDEX_Alphanumeric:
2101 return CODE_NAME_INDEX_CapsLock;
2102 case KEY_NAME_INDEX_HiraganaKatakana:
2103 return CODE_NAME_INDEX_KanaMode;
2104 case KEY_NAME_INDEX_ZenkakuHankaku:
2105 return CODE_NAME_INDEX_Backquote;
2106 #endif // #ifdef MOZ_WIDGET_GTK
2108 #ifdef ANDROID
2109 case KEY_NAME_INDEX_Convert:
2110 return CODE_NAME_INDEX_Convert;
2111 case KEY_NAME_INDEX_NonConvert:
2112 return CODE_NAME_INDEX_NonConvert;
2113 case KEY_NAME_INDEX_HiraganaKatakana:
2114 return CODE_NAME_INDEX_KanaMode;
2115 case KEY_NAME_INDEX_ZenkakuHankaku:
2116 return CODE_NAME_INDEX_Backquote;
2117 case KEY_NAME_INDEX_Eisu:
2118 return CODE_NAME_INDEX_Lang2;
2119 case KEY_NAME_INDEX_KanjiMode:
2120 return CODE_NAME_INDEX_Lang1;
2121 #endif // #ifdef ANDROID
2123 #ifdef XP_MACOSX
2124 case KEY_NAME_INDEX_Eisu:
2125 return CODE_NAME_INDEX_Lang2;
2126 case KEY_NAME_INDEX_KanjiMode:
2127 return CODE_NAME_INDEX_Lang1;
2128 #endif // #ifdef XP_MACOSX
2130 default:
2131 return CODE_NAME_INDEX_UNKNOWN;
2135 /* static */
2136 Modifier WidgetKeyboardEvent::GetModifierForKeyName(
2137 KeyNameIndex aKeyNameIndex) {
2138 switch (aKeyNameIndex) {
2139 case KEY_NAME_INDEX_Alt:
2140 return MODIFIER_ALT;
2141 case KEY_NAME_INDEX_AltGraph:
2142 return MODIFIER_ALTGRAPH;
2143 case KEY_NAME_INDEX_CapsLock:
2144 return MODIFIER_CAPSLOCK;
2145 case KEY_NAME_INDEX_Control:
2146 return MODIFIER_CONTROL;
2147 case KEY_NAME_INDEX_Fn:
2148 return MODIFIER_FN;
2149 case KEY_NAME_INDEX_FnLock:
2150 return MODIFIER_FNLOCK;
2151 // case KEY_NAME_INDEX_Hyper:
2152 case KEY_NAME_INDEX_Meta:
2153 return MODIFIER_META;
2154 case KEY_NAME_INDEX_NumLock:
2155 return MODIFIER_NUMLOCK;
2156 case KEY_NAME_INDEX_ScrollLock:
2157 return MODIFIER_SCROLLLOCK;
2158 case KEY_NAME_INDEX_Shift:
2159 return MODIFIER_SHIFT;
2160 // case KEY_NAME_INDEX_Super:
2161 case KEY_NAME_INDEX_Symbol:
2162 return MODIFIER_SYMBOL;
2163 case KEY_NAME_INDEX_SymbolLock:
2164 return MODIFIER_SYMBOLLOCK;
2165 default:
2166 return MODIFIER_NONE;
2170 /* static */
2171 bool WidgetKeyboardEvent::IsLockableModifier(KeyNameIndex aKeyNameIndex) {
2172 switch (aKeyNameIndex) {
2173 case KEY_NAME_INDEX_CapsLock:
2174 case KEY_NAME_INDEX_FnLock:
2175 case KEY_NAME_INDEX_NumLock:
2176 case KEY_NAME_INDEX_ScrollLock:
2177 case KEY_NAME_INDEX_SymbolLock:
2178 return true;
2179 default:
2180 return false;
2184 /******************************************************************************
2185 * mozilla::InternalEditorInputEvent (TextEvents.h)
2186 ******************************************************************************/
2188 #define NS_DEFINE_INPUTTYPE(aCPPName, aDOMName) (u"" aDOMName),
2189 const char16_t* const InternalEditorInputEvent::kInputTypeNames[] = {
2190 #include "mozilla/InputTypeList.h"
2192 #undef NS_DEFINE_INPUTTYPE
2194 InternalEditorInputEvent::InputTypeHashtable*
2195 InternalEditorInputEvent::sInputTypeHashtable = nullptr;
2197 /* static */
2198 void InternalEditorInputEvent::Shutdown() {
2199 delete sInputTypeHashtable;
2200 sInputTypeHashtable = nullptr;
2203 /* static */
2204 void InternalEditorInputEvent::GetDOMInputTypeName(EditorInputType aInputType,
2205 nsAString& aInputTypeName) {
2206 if (static_cast<size_t>(aInputType) >=
2207 static_cast<size_t>(EditorInputType::eUnknown)) {
2208 aInputTypeName.Truncate();
2209 return;
2212 MOZ_RELEASE_ASSERT(
2213 static_cast<size_t>(aInputType) < std::size(kInputTypeNames),
2214 "Illegal input type enumeration value");
2215 aInputTypeName.Assign(kInputTypeNames[static_cast<size_t>(aInputType)]);
2218 /* static */
2219 EditorInputType InternalEditorInputEvent::GetEditorInputType(
2220 const nsAString& aInputType) {
2221 if (aInputType.IsEmpty()) {
2222 return EditorInputType::eUnknown;
2225 if (!sInputTypeHashtable) {
2226 sInputTypeHashtable = new InputTypeHashtable(std::size(kInputTypeNames));
2227 for (size_t i = 0; i < std::size(kInputTypeNames); i++) {
2228 sInputTypeHashtable->InsertOrUpdate(nsDependentString(kInputTypeNames[i]),
2229 static_cast<EditorInputType>(i));
2232 return sInputTypeHashtable->MaybeGet(aInputType)
2233 .valueOr(EditorInputType::eUnknown);
2236 } // namespace mozilla