/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gfxPrefs.h" #include "mozilla/BasicEvents.h" #include "mozilla/ContentEvents.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include "nsIDOMEventTarget.h" #include "nsPrintfCString.h" namespace mozilla { /****************************************************************************** * Global helper methods ******************************************************************************/ const char* ToChar(EventMessage aEventMessage) { switch (aEventMessage) { #define NS_EVENT_MESSAGE(aMessage) \ case aMessage: \ return #aMessage; #include "mozilla/EventMessageList.h" #undef NS_EVENT_MESSAGE default: return "illegal event message"; } } const char* ToChar(EventClassID aEventClassID) { switch (aEventClassID) { #define NS_ROOT_EVENT_CLASS(aPrefix, aName) \ case eBasic##aName##Class: \ return "eBasic" #aName "Class"; #define NS_EVENT_CLASS(aPrefix, aName) \ case e##aName##Class: \ return "e" #aName "Class"; #include "mozilla/EventClassList.h" #undef NS_EVENT_CLASS #undef NS_ROOT_EVENT_CLASS default: return "illegal event class ID"; } } const nsCString ToString(KeyNameIndex aKeyNameIndex) { if (aKeyNameIndex == KEY_NAME_INDEX_USE_STRING) { return NS_LITERAL_CSTRING("USE_STRING"); } nsAutoString keyName; WidgetKeyboardEvent::GetDOMKeyName(aKeyNameIndex, keyName); return NS_ConvertUTF16toUTF8(keyName); } const nsCString ToString(CodeNameIndex aCodeNameIndex) { if (aCodeNameIndex == CODE_NAME_INDEX_USE_STRING) { return NS_LITERAL_CSTRING("USE_STRING"); } nsAutoString codeName; WidgetKeyboardEvent::GetDOMCodeName(aCodeNameIndex, codeName); return NS_ConvertUTF16toUTF8(codeName); } const nsCString GetDOMKeyCodeName(uint32_t aKeyCode) { switch (aKeyCode) { #define NS_DISALLOW_SAME_KEYCODE #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ case aDOMKeyCode: \ return NS_LITERAL_CSTRING(#aDOMKeyName); #include "mozilla/VirtualKeyCodeList.h" #undef NS_DEFINE_VK #undef NS_DISALLOW_SAME_KEYCODE default: return nsPrintfCString("Invalid DOM keyCode (0x%08X)", aKeyCode); } } bool IsValidRawTextRangeValue(RawTextRangeType aRawTextRangeType) { switch (static_cast(aRawTextRangeType)) { case TextRangeType::eUninitialized: case TextRangeType::eCaret: case TextRangeType::eRawClause: case TextRangeType::eSelectedRawClause: case TextRangeType::eConvertedClause: case TextRangeType::eSelectedClause: return true; default: return false; } } RawTextRangeType ToRawTextRangeType(TextRangeType aTextRangeType) { return static_cast(aTextRangeType); } TextRangeType ToTextRangeType(RawTextRangeType aRawTextRangeType) { MOZ_ASSERT(IsValidRawTextRangeValue(aRawTextRangeType)); return static_cast(aRawTextRangeType); } const char* ToChar(TextRangeType aTextRangeType) { switch (aTextRangeType) { case TextRangeType::eUninitialized: return "TextRangeType::eUninitialized"; case TextRangeType::eCaret: return "TextRangeType::eCaret"; case TextRangeType::eRawClause: return "TextRangeType::eRawClause"; case TextRangeType::eSelectedRawClause: return "TextRangeType::eSelectedRawClause"; case TextRangeType::eConvertedClause: return "TextRangeType::eConvertedClause"; case TextRangeType::eSelectedClause: return "TextRangeType::eSelectedClause"; default: return "Invalid TextRangeType"; } } SelectionType ToSelectionType(TextRangeType aTextRangeType) { switch (aTextRangeType) { case TextRangeType::eRawClause: return SelectionType::eIMERawClause; case TextRangeType::eSelectedRawClause: return SelectionType::eIMESelectedRawClause; case TextRangeType::eConvertedClause: return SelectionType::eIMEConvertedClause; case TextRangeType::eSelectedClause: return SelectionType::eIMESelectedClause; default: MOZ_CRASH("TextRangeType is invalid"); return SelectionType::eNormal; } } /****************************************************************************** * As*Event() implementation ******************************************************************************/ #define NS_ROOT_EVENT_CLASS(aPrefix, aName) #define NS_EVENT_CLASS(aPrefix, aName) \ aPrefix##aName* \ WidgetEvent::As##aName() \ { \ return nullptr; \ } \ \ const aPrefix##aName* \ WidgetEvent::As##aName() const \ { \ return const_cast(this)->As##aName(); \ } #include "mozilla/EventClassList.h" #undef NS_EVENT_CLASS #undef NS_ROOT_EVENT_CLASS /****************************************************************************** * mozilla::WidgetEvent * * Event struct type checking methods. ******************************************************************************/ bool WidgetEvent::IsQueryContentEvent() const { return mClass == eQueryContentEventClass; } bool WidgetEvent::IsSelectionEvent() const { return mClass == eSelectionEventClass; } bool WidgetEvent::IsContentCommandEvent() const { return mClass == eContentCommandEventClass; } bool WidgetEvent::IsNativeEventDelivererForPlugin() const { return mClass == ePluginEventClass; } /****************************************************************************** * mozilla::WidgetEvent * * Event message checking methods. ******************************************************************************/ bool WidgetEvent::HasMouseEventMessage() const { switch (mMessage) { case eMouseDown: case eMouseUp: case eMouseClick: case eMouseDoubleClick: case eMouseAuxClick: case eMouseEnterIntoWidget: case eMouseExitFromWidget: case eMouseActivate: case eMouseOver: case eMouseOut: case eMouseHitTest: case eMouseMove: return true; default: return false; } } bool WidgetEvent::HasDragEventMessage() const { switch (mMessage) { case eDragEnter: case eDragOver: case eDragExit: case eDrag: case eDragEnd: case eDragStart: case eDrop: case eDragLeave: return true; default: return false; } } bool WidgetEvent::HasKeyEventMessage() const { switch (mMessage) { case eKeyDown: case eKeyPress: case eKeyUp: case eKeyDownOnPlugin: case eKeyUpOnPlugin: case eAccessKeyNotFound: return true; default: return false; } } bool WidgetEvent::HasIMEEventMessage() const { switch (mMessage) { case eCompositionStart: case eCompositionEnd: case eCompositionUpdate: case eCompositionChange: case eCompositionCommitAsIs: case eCompositionCommit: return true; default: return false; } } bool WidgetEvent::HasPluginActivationEventMessage() const { return mMessage == ePluginActivate || mMessage == ePluginFocus; } /****************************************************************************** * mozilla::WidgetEvent * * Specific event checking methods. ******************************************************************************/ bool WidgetEvent::CanBeSentToRemoteProcess() const { // If this event is explicitly marked as shouldn't be sent to remote process, // just return false. if (mFlags.mNoCrossProcessBoundaryForwarding) { return false; } if (mClass == eKeyboardEventClass || mClass == eWheelEventClass) { return true; } switch (mMessage) { case eMouseDown: case eMouseUp: case eMouseMove: case eContextMenu: case eMouseEnterIntoWidget: case eMouseExitFromWidget: case eMouseTouchDrag: case eTouchStart: case eTouchMove: case eTouchEnd: case eTouchCancel: case eDragOver: case eDragExit: case eDrop: return true; default: return false; } } bool WidgetEvent::IsRetargetedNativeEventDelivererForPlugin() const { const WidgetPluginEvent* pluginEvent = AsPluginEvent(); return pluginEvent && pluginEvent->mRetargetToFocusedDocument; } bool WidgetEvent::IsNonRetargetedNativeEventDelivererForPlugin() const { const WidgetPluginEvent* pluginEvent = AsPluginEvent(); return pluginEvent && !pluginEvent->mRetargetToFocusedDocument; } bool WidgetEvent::IsIMERelatedEvent() const { return HasIMEEventMessage() || IsQueryContentEvent() || IsSelectionEvent(); } bool WidgetEvent::IsUsingCoordinates() const { const WidgetMouseEvent* mouseEvent = AsMouseEvent(); if (mouseEvent) { return !mouseEvent->IsContextMenuKeyEvent(); } return !HasKeyEventMessage() && !IsIMERelatedEvent() && !HasPluginActivationEventMessage() && !IsNativeEventDelivererForPlugin() && !IsContentCommandEvent(); } bool WidgetEvent::IsTargetedAtFocusedWindow() const { const WidgetMouseEvent* mouseEvent = AsMouseEvent(); if (mouseEvent) { return mouseEvent->IsContextMenuKeyEvent(); } return HasKeyEventMessage() || IsIMERelatedEvent() || IsContentCommandEvent() || IsRetargetedNativeEventDelivererForPlugin(); } bool WidgetEvent::IsTargetedAtFocusedContent() const { const WidgetMouseEvent* mouseEvent = AsMouseEvent(); if (mouseEvent) { return mouseEvent->IsContextMenuKeyEvent(); } return HasKeyEventMessage() || IsIMERelatedEvent() || IsRetargetedNativeEventDelivererForPlugin(); } bool WidgetEvent::IsAllowedToDispatchDOMEvent() const { switch (mClass) { case eMouseEventClass: // When content PreventDefault on ePointerDown, we will stop dispatching // the subsequent mouse events (eMouseDown, eMouseUp, eMouseMove). But we // still need the mouse events to be handled in EventStateManager to // generate other events (e.g. eMouseClick). So we only stop dispatching // them to DOM. if (DefaultPreventedByContent() && (mMessage == eMouseMove || mMessage == eMouseDown || mMessage == eMouseUp)) { return false; } if (mMessage == eMouseTouchDrag) { return false; } MOZ_FALLTHROUGH; case ePointerEventClass: // We want synthesized mouse moves to cause mouseover and mouseout // DOM events (EventStateManager::PreHandleEvent), but not mousemove // DOM events. // Synthesized button up events also do not cause DOM events because they // do not have a reliable mRefPoint. return AsMouseEvent()->mReason == WidgetMouseEvent::eReal; case eWheelEventClass: { // wheel event whose all delta values are zero by user pref applied, it // shouldn't cause a DOM event. const WidgetWheelEvent* wheelEvent = AsWheelEvent(); return wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0 || wheelEvent->mDeltaZ != 0.0; } case eTouchEventClass: return mMessage != eTouchPointerCancel; // Following events are handled in EventStateManager, so, we don't need to // dispatch DOM event for them into the DOM tree. case eQueryContentEventClass: case eSelectionEventClass: case eContentCommandEventClass: return false; default: return true; } } bool WidgetEvent::IsAllowedToDispatchInSystemGroup() const { // We don't expect to implement default behaviors with pointer events because // if we do, prevent default on mouse events can't prevent default behaviors // anymore. return mClass != ePointerEventClass; } /****************************************************************************** * mozilla::WidgetEvent * * Misc methods. ******************************************************************************/ static dom::EventTarget* GetTargetForDOMEvent(nsIDOMEventTarget* aTarget) { return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr; } dom::EventTarget* WidgetEvent::GetDOMEventTarget() const { return GetTargetForDOMEvent(mTarget); } dom::EventTarget* WidgetEvent::GetCurrentDOMEventTarget() const { return GetTargetForDOMEvent(mCurrentTarget); } dom::EventTarget* WidgetEvent::GetOriginalDOMEventTarget() const { if (mOriginalTarget) { return GetTargetForDOMEvent(mOriginalTarget); } return GetDOMEventTarget(); } /****************************************************************************** * mozilla::WidgetInputEvent ******************************************************************************/ /* static */ Modifier WidgetInputEvent::GetModifier(const nsAString& aDOMKeyName) { if (aDOMKeyName.EqualsLiteral("Accel")) { return AccelModifier(); } KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aDOMKeyName); return WidgetKeyboardEvent::GetModifierForKeyName(keyNameIndex); } /* static */ Modifier WidgetInputEvent::AccelModifier() { static Modifier sAccelModifier = MODIFIER_NONE; if (sAccelModifier == MODIFIER_NONE) { switch (Preferences::GetInt("ui.key.accelKey", 0)) { case nsIDOMKeyEvent::DOM_VK_META: sAccelModifier = MODIFIER_META; break; case nsIDOMKeyEvent::DOM_VK_WIN: sAccelModifier = MODIFIER_OS; break; case nsIDOMKeyEvent::DOM_VK_ALT: sAccelModifier = MODIFIER_ALT; break; case nsIDOMKeyEvent::DOM_VK_CONTROL: sAccelModifier = MODIFIER_CONTROL; break; default: #ifdef XP_MACOSX sAccelModifier = MODIFIER_META; #else sAccelModifier = MODIFIER_CONTROL; #endif break; } } return sAccelModifier; } /****************************************************************************** * mozilla::WidgetWheelEvent (MouseEvents.h) ******************************************************************************/ /* static */ double WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, bool aIsForVertical) { if (!gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) { return aDelta; } int32_t intFactor = aIsForVertical ? gfxPrefs::MouseWheelRootScrollVerticalFactor() : gfxPrefs::MouseWheelRootScrollHorizontalFactor(); // Making the scroll speed slower doesn't make sense. So, ignore odd factor // which is less than 1.0. if (intFactor <= 100) { return aDelta; } double factor = static_cast(intFactor) / 100; return aDelta * factor; } double WidgetWheelEvent::OverriddenDeltaX() const { if (!mAllowToOverrideSystemScrollSpeed) { return mDeltaX; } return ComputeOverriddenDelta(mDeltaX, false); } double WidgetWheelEvent::OverriddenDeltaY() const { if (!mAllowToOverrideSystemScrollSpeed) { return mDeltaY; } return ComputeOverriddenDelta(mDeltaY, true); } /****************************************************************************** * mozilla::WidgetKeyboardEvent (TextEvents.h) ******************************************************************************/ #define NS_DEFINE_KEYNAME(aCPPName, aDOMKeyName) (u"" aDOMKeyName), const char16_t* const WidgetKeyboardEvent::kKeyNames[] = { #include "mozilla/KeyNameList.h" }; #undef NS_DEFINE_KEYNAME #define NS_DEFINE_PHYSICAL_KEY_CODE_NAME(aCPPName, aDOMCodeName) \ (u"" aDOMCodeName), const char16_t* const WidgetKeyboardEvent::kCodeNames[] = { #include "mozilla/PhysicalKeyCodeNameList.h" }; #undef NS_DEFINE_PHYSICAL_KEY_CODE_NAME WidgetKeyboardEvent::KeyNameIndexHashtable* WidgetKeyboardEvent::sKeyNameIndexHashtable = nullptr; WidgetKeyboardEvent::CodeNameIndexHashtable* WidgetKeyboardEvent::sCodeNameIndexHashtable = nullptr; void WidgetKeyboardEvent::InitAllEditCommands() { // If the event was created without widget, e.g., created event in chrome // script, this shouldn't execute native key bindings. if (NS_WARN_IF(!mWidget)) { return; } // This event should be trusted event here and we shouldn't expose native // key binding information to web contents with untrusted events. if (NS_WARN_IF(!IsTrusted())) { return; } MOZ_ASSERT(XRE_IsParentProcess(), "It's too expensive to retrieve all edit commands from remote process"); MOZ_ASSERT(!AreAllEditCommandsInitialized(), "Shouldn't be called two or more times"); InitEditCommandsFor(nsIWidget::NativeKeyBindingsForSingleLineEditor); InitEditCommandsFor(nsIWidget::NativeKeyBindingsForMultiLineEditor); InitEditCommandsFor(nsIWidget::NativeKeyBindingsForRichTextEditor); } void WidgetKeyboardEvent::InitEditCommandsFor(nsIWidget::NativeKeyBindingsType aType) { if (NS_WARN_IF(!mWidget) || NS_WARN_IF(!IsTrusted())) { return; } bool& initialized = IsEditCommandsInitializedRef(aType); if (initialized) { return; } nsTArray& commands = EditCommandsRef(aType); mWidget->GetEditCommands(aType, *this, commands); initialized = true; } bool WidgetKeyboardEvent::ExecuteEditCommands(nsIWidget::NativeKeyBindingsType aType, DoCommandCallback aCallback, void* aCallbackData) { // If the event was created without widget, e.g., created event in chrome // script, this shouldn't execute native key bindings. if (NS_WARN_IF(!mWidget)) { return false; } // This event should be trusted event here and we shouldn't expose native // key binding information to web contents with untrusted events. if (NS_WARN_IF(!IsTrusted())) { return false; } InitEditCommandsFor(aType); const nsTArray& commands = EditCommandsRef(aType); if (commands.IsEmpty()) { return false; } for (CommandInt command : commands) { aCallback(static_cast(command), aCallbackData); } return true; } bool WidgetKeyboardEvent::ShouldCauseKeypressEvents() const { // Currently, we don't dispatch keypress events of modifier keys and // dead keys. switch (mKeyNameIndex) { case KEY_NAME_INDEX_Alt: case KEY_NAME_INDEX_AltGraph: case KEY_NAME_INDEX_CapsLock: case KEY_NAME_INDEX_Control: case KEY_NAME_INDEX_Fn: case KEY_NAME_INDEX_FnLock: // case KEY_NAME_INDEX_Hyper: case KEY_NAME_INDEX_Meta: case KEY_NAME_INDEX_NumLock: case KEY_NAME_INDEX_OS: case KEY_NAME_INDEX_ScrollLock: case KEY_NAME_INDEX_Shift: // case KEY_NAME_INDEX_Super: case KEY_NAME_INDEX_Symbol: case KEY_NAME_INDEX_SymbolLock: case KEY_NAME_INDEX_Dead: return false; default: return true; } } static bool HasASCIIDigit(const ShortcutKeyCandidateArray& aCandidates) { for (uint32_t i = 0; i < aCandidates.Length(); ++i) { uint32_t ch = aCandidates[i].mCharCode; if (ch >= '0' && ch <= '9') return true; } return false; } static bool CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2) { return aChar1 == aChar2 || (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) && ToLowerCase(static_cast(aChar1)) == ToLowerCase(static_cast(aChar2))); } static bool IsCaseChangeableChar(uint32_t aChar) { return IS_IN_BMP(aChar) && ToLowerCase(static_cast(aChar)) != ToUpperCase(static_cast(aChar)); } void WidgetKeyboardEvent::GetShortcutKeyCandidates( ShortcutKeyCandidateArray& aCandidates) { MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty"); // ShortcutKeyCandidate::mCharCode is a candidate charCode. // ShortcutKeyCandidate::mIgnoreShift means the mCharCode should be tried to // execute a command with/without shift key state. If this is TRUE, the // shifted key state should be ignored. Otherwise, don't ignore the state. // the priority of the charCodes are (shift key is not pressed): // 0: PseudoCharCode()/false, // 1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false... // the priority of the charCodes are (shift key is pressed): // 0: PseudoCharCode()/false, // 1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true, // 3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true... uint32_t pseudoCharCode = PseudoCharCode(); if (pseudoCharCode) { ShortcutKeyCandidate key(pseudoCharCode, false); aCandidates.AppendElement(key); } uint32_t len = mAlternativeCharCodes.Length(); if (!IsShift()) { for (uint32_t i = 0; i < len; ++i) { uint32_t ch = mAlternativeCharCodes[i].mUnshiftedCharCode; if (!ch || ch == pseudoCharCode) { continue; } ShortcutKeyCandidate key(ch, false); aCandidates.AppendElement(key); } // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it, // this keyboard layout is AZERTY or similar layout, probably. // In this case, Accel+[0-9] should be accessible without shift key. // However, the priority should be lowest. if (!HasASCIIDigit(aCandidates)) { for (uint32_t i = 0; i < len; ++i) { uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode; if (ch >= '0' && ch <= '9') { ShortcutKeyCandidate key(ch, false); aCandidates.AppendElement(key); break; } } } } else { for (uint32_t i = 0; i < len; ++i) { uint32_t ch = mAlternativeCharCodes[i].mShiftedCharCode; if (!ch) { continue; } if (ch != pseudoCharCode) { ShortcutKeyCandidate key(ch, false); aCandidates.AppendElement(key); } // If the char is an alphabet, the shift key state should not be // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C. // And checking the charCode is same as unshiftedCharCode too. // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus. uint32_t unshiftCh = mAlternativeCharCodes[i].mUnshiftedCharCode; if (CharsCaseInsensitiveEqual(ch, unshiftCh)) { continue; } // On the Hebrew keyboard layout on Windows, the unshifted char is a // localized character but the shifted char is a Latin alphabet, // then, we should not execute without the shift state. See bug 433192. if (IsCaseChangeableChar(ch)) { continue; } // Setting the alternative charCode candidates for retry without shift // key state only when the shift key is pressed. ShortcutKeyCandidate key(ch, true); aCandidates.AppendElement(key); } } // Special case for "Space" key. With some keyboard layouts, "Space" with // or without Shift key causes non-ASCII space. For such keyboard layouts, // we should guarantee that the key press works as an ASCII white space key // press. However, if the space key is assigned to a function key, it // shouldn't work as a space key. if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING && mCodeNameIndex == CODE_NAME_INDEX_Space && pseudoCharCode != ' ') { ShortcutKeyCandidate spaceKey(' ', false); aCandidates.AppendElement(spaceKey); } } void WidgetKeyboardEvent::GetAccessKeyCandidates(nsTArray& aCandidates) { MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty"); // return the lower cased charCode candidates for access keys. // the priority of the charCodes are: // 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0] // 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],... if (mCharCode) { uint32_t ch = mCharCode; if (IS_IN_BMP(ch)) { ch = ToLowerCase(static_cast(ch)); } aCandidates.AppendElement(ch); } for (uint32_t i = 0; i < mAlternativeCharCodes.Length(); ++i) { uint32_t ch[2] = { mAlternativeCharCodes[i].mUnshiftedCharCode, mAlternativeCharCodes[i].mShiftedCharCode }; for (uint32_t j = 0; j < 2; ++j) { if (!ch[j]) { continue; } if (IS_IN_BMP(ch[j])) { ch[j] = ToLowerCase(static_cast(ch[j])); } // Don't append the mCharCode that was already appended. if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex) { aCandidates.AppendElement(ch[j]); } } } // Special case for "Space" key. With some keyboard layouts, "Space" with // or without Shift key causes non-ASCII space. For such keyboard layouts, // we should guarantee that the key press works as an ASCII white space key // press. However, if the space key is assigned to a function key, it // shouldn't work as a space key. if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING && mCodeNameIndex == CODE_NAME_INDEX_Space && mCharCode != ' ') { aCandidates.AppendElement(' '); } return; } /* static */ void WidgetKeyboardEvent::Shutdown() { delete sKeyNameIndexHashtable; sKeyNameIndexHashtable = nullptr; delete sCodeNameIndexHashtable; sCodeNameIndexHashtable = nullptr; } /* static */ void WidgetKeyboardEvent::GetDOMKeyName(KeyNameIndex aKeyNameIndex, nsAString& aKeyName) { if (aKeyNameIndex >= KEY_NAME_INDEX_USE_STRING) { aKeyName.Truncate(); return; } MOZ_RELEASE_ASSERT(static_cast(aKeyNameIndex) < ArrayLength(kKeyNames), "Illegal key enumeration value"); aKeyName = kKeyNames[aKeyNameIndex]; } /* static */ void WidgetKeyboardEvent::GetDOMCodeName(CodeNameIndex aCodeNameIndex, nsAString& aCodeName) { if (aCodeNameIndex >= CODE_NAME_INDEX_USE_STRING) { aCodeName.Truncate(); return; } MOZ_RELEASE_ASSERT(static_cast(aCodeNameIndex) < ArrayLength(kCodeNames), "Illegal physical code enumeration value"); aCodeName = kCodeNames[aCodeNameIndex]; } /* static */ KeyNameIndex WidgetKeyboardEvent::GetKeyNameIndex(const nsAString& aKeyValue) { if (!sKeyNameIndexHashtable) { sKeyNameIndexHashtable = new KeyNameIndexHashtable(ArrayLength(kKeyNames)); for (size_t i = 0; i < ArrayLength(kKeyNames); i++) { sKeyNameIndexHashtable->Put(nsDependentString(kKeyNames[i]), static_cast(i)); } } KeyNameIndex result = KEY_NAME_INDEX_USE_STRING; sKeyNameIndexHashtable->Get(aKeyValue, &result); return result; } /* static */ CodeNameIndex WidgetKeyboardEvent::GetCodeNameIndex(const nsAString& aCodeValue) { if (!sCodeNameIndexHashtable) { sCodeNameIndexHashtable = new CodeNameIndexHashtable(ArrayLength(kCodeNames)); for (size_t i = 0; i < ArrayLength(kCodeNames); i++) { sCodeNameIndexHashtable->Put(nsDependentString(kCodeNames[i]), static_cast(i)); } } CodeNameIndex result = CODE_NAME_INDEX_USE_STRING; sCodeNameIndexHashtable->Get(aCodeValue, &result); return result; } /* static */ const char* WidgetKeyboardEvent::GetCommandStr(Command aCommand) { #define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr static const char* const kCommands[] = { "" // CommandDoNothing #include "mozilla/CommandList.h" }; #undef NS_DEFINE_COMMAND MOZ_RELEASE_ASSERT(static_cast(aCommand) < ArrayLength(kCommands), "Illegal command enumeration value"); return kCommands[aCommand]; } /* static */ uint32_t WidgetKeyboardEvent::ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex) { // Following commented out cases are not defined in PhysicalKeyCodeNameList.h // but are defined by D3E spec. So, they should be uncommented when the // code values are defined in the header. switch (aCodeNameIndex) { case CODE_NAME_INDEX_AltLeft: case CODE_NAME_INDEX_ControlLeft: case CODE_NAME_INDEX_OSLeft: case CODE_NAME_INDEX_ShiftLeft: return eKeyLocationLeft; case CODE_NAME_INDEX_AltRight: case CODE_NAME_INDEX_ControlRight: case CODE_NAME_INDEX_OSRight: case CODE_NAME_INDEX_ShiftRight: return eKeyLocationRight; case CODE_NAME_INDEX_Numpad0: case CODE_NAME_INDEX_Numpad1: case CODE_NAME_INDEX_Numpad2: case CODE_NAME_INDEX_Numpad3: case CODE_NAME_INDEX_Numpad4: case CODE_NAME_INDEX_Numpad5: case CODE_NAME_INDEX_Numpad6: case CODE_NAME_INDEX_Numpad7: case CODE_NAME_INDEX_Numpad8: case CODE_NAME_INDEX_Numpad9: case CODE_NAME_INDEX_NumpadAdd: case CODE_NAME_INDEX_NumpadBackspace: case CODE_NAME_INDEX_NumpadClear: case CODE_NAME_INDEX_NumpadClearEntry: case CODE_NAME_INDEX_NumpadComma: case CODE_NAME_INDEX_NumpadDecimal: case CODE_NAME_INDEX_NumpadDivide: case CODE_NAME_INDEX_NumpadEnter: case CODE_NAME_INDEX_NumpadEqual: case CODE_NAME_INDEX_NumpadMemoryAdd: case CODE_NAME_INDEX_NumpadMemoryClear: case CODE_NAME_INDEX_NumpadMemoryRecall: case CODE_NAME_INDEX_NumpadMemoryStore: case CODE_NAME_INDEX_NumpadMemorySubtract: case CODE_NAME_INDEX_NumpadMultiply: case CODE_NAME_INDEX_NumpadParenLeft: case CODE_NAME_INDEX_NumpadParenRight: case CODE_NAME_INDEX_NumpadSubtract: return eKeyLocationNumpad; default: return eKeyLocationStandard; } } /* static */ uint32_t WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex) { switch (aKeyNameIndex) { case KEY_NAME_INDEX_Cancel: return nsIDOMKeyEvent::DOM_VK_CANCEL; case KEY_NAME_INDEX_Help: return nsIDOMKeyEvent::DOM_VK_HELP; case KEY_NAME_INDEX_Backspace: return nsIDOMKeyEvent::DOM_VK_BACK_SPACE; case KEY_NAME_INDEX_Tab: return nsIDOMKeyEvent::DOM_VK_TAB; case KEY_NAME_INDEX_Clear: return nsIDOMKeyEvent::DOM_VK_CLEAR; case KEY_NAME_INDEX_Enter: return nsIDOMKeyEvent::DOM_VK_RETURN; case KEY_NAME_INDEX_Shift: return nsIDOMKeyEvent::DOM_VK_SHIFT; case KEY_NAME_INDEX_Control: return nsIDOMKeyEvent::DOM_VK_CONTROL; case KEY_NAME_INDEX_Alt: return nsIDOMKeyEvent::DOM_VK_ALT; case KEY_NAME_INDEX_Pause: return nsIDOMKeyEvent::DOM_VK_PAUSE; case KEY_NAME_INDEX_CapsLock: return nsIDOMKeyEvent::DOM_VK_CAPS_LOCK; case KEY_NAME_INDEX_Hiragana: case KEY_NAME_INDEX_Katakana: case KEY_NAME_INDEX_HiraganaKatakana: case KEY_NAME_INDEX_KanaMode: return nsIDOMKeyEvent::DOM_VK_KANA; case KEY_NAME_INDEX_HangulMode: return nsIDOMKeyEvent::DOM_VK_HANGUL; case KEY_NAME_INDEX_Eisu: return nsIDOMKeyEvent::DOM_VK_EISU; case KEY_NAME_INDEX_JunjaMode: return nsIDOMKeyEvent::DOM_VK_JUNJA; case KEY_NAME_INDEX_FinalMode: return nsIDOMKeyEvent::DOM_VK_FINAL; case KEY_NAME_INDEX_HanjaMode: return nsIDOMKeyEvent::DOM_VK_HANJA; case KEY_NAME_INDEX_KanjiMode: return nsIDOMKeyEvent::DOM_VK_KANJI; case KEY_NAME_INDEX_Escape: return nsIDOMKeyEvent::DOM_VK_ESCAPE; case KEY_NAME_INDEX_Convert: return nsIDOMKeyEvent::DOM_VK_CONVERT; case KEY_NAME_INDEX_NonConvert: return nsIDOMKeyEvent::DOM_VK_NONCONVERT; case KEY_NAME_INDEX_Accept: return nsIDOMKeyEvent::DOM_VK_ACCEPT; case KEY_NAME_INDEX_ModeChange: return nsIDOMKeyEvent::DOM_VK_MODECHANGE; case KEY_NAME_INDEX_PageUp: return nsIDOMKeyEvent::DOM_VK_PAGE_UP; case KEY_NAME_INDEX_PageDown: return nsIDOMKeyEvent::DOM_VK_PAGE_DOWN; case KEY_NAME_INDEX_End: return nsIDOMKeyEvent::DOM_VK_END; case KEY_NAME_INDEX_Home: return nsIDOMKeyEvent::DOM_VK_HOME; case KEY_NAME_INDEX_ArrowLeft: return nsIDOMKeyEvent::DOM_VK_LEFT; case KEY_NAME_INDEX_ArrowUp: return nsIDOMKeyEvent::DOM_VK_UP; case KEY_NAME_INDEX_ArrowRight: return nsIDOMKeyEvent::DOM_VK_RIGHT; case KEY_NAME_INDEX_ArrowDown: return nsIDOMKeyEvent::DOM_VK_DOWN; case KEY_NAME_INDEX_Select: return nsIDOMKeyEvent::DOM_VK_SELECT; case KEY_NAME_INDEX_Print: return nsIDOMKeyEvent::DOM_VK_PRINT; case KEY_NAME_INDEX_Execute: return nsIDOMKeyEvent::DOM_VK_EXECUTE; case KEY_NAME_INDEX_PrintScreen: return nsIDOMKeyEvent::DOM_VK_PRINTSCREEN; case KEY_NAME_INDEX_Insert: return nsIDOMKeyEvent::DOM_VK_INSERT; case KEY_NAME_INDEX_Delete: return nsIDOMKeyEvent::DOM_VK_DELETE; case KEY_NAME_INDEX_OS: // case KEY_NAME_INDEX_Super: // case KEY_NAME_INDEX_Hyper: return nsIDOMKeyEvent::DOM_VK_WIN; case KEY_NAME_INDEX_ContextMenu: return nsIDOMKeyEvent::DOM_VK_CONTEXT_MENU; case KEY_NAME_INDEX_Standby: return nsIDOMKeyEvent::DOM_VK_SLEEP; case KEY_NAME_INDEX_F1: return nsIDOMKeyEvent::DOM_VK_F1; case KEY_NAME_INDEX_F2: return nsIDOMKeyEvent::DOM_VK_F2; case KEY_NAME_INDEX_F3: return nsIDOMKeyEvent::DOM_VK_F3; case KEY_NAME_INDEX_F4: return nsIDOMKeyEvent::DOM_VK_F4; case KEY_NAME_INDEX_F5: return nsIDOMKeyEvent::DOM_VK_F5; case KEY_NAME_INDEX_F6: return nsIDOMKeyEvent::DOM_VK_F6; case KEY_NAME_INDEX_F7: return nsIDOMKeyEvent::DOM_VK_F7; case KEY_NAME_INDEX_F8: return nsIDOMKeyEvent::DOM_VK_F8; case KEY_NAME_INDEX_F9: return nsIDOMKeyEvent::DOM_VK_F9; case KEY_NAME_INDEX_F10: return nsIDOMKeyEvent::DOM_VK_F10; case KEY_NAME_INDEX_F11: return nsIDOMKeyEvent::DOM_VK_F11; case KEY_NAME_INDEX_F12: return nsIDOMKeyEvent::DOM_VK_F12; case KEY_NAME_INDEX_F13: return nsIDOMKeyEvent::DOM_VK_F13; case KEY_NAME_INDEX_F14: return nsIDOMKeyEvent::DOM_VK_F14; case KEY_NAME_INDEX_F15: return nsIDOMKeyEvent::DOM_VK_F15; case KEY_NAME_INDEX_F16: return nsIDOMKeyEvent::DOM_VK_F16; case KEY_NAME_INDEX_F17: return nsIDOMKeyEvent::DOM_VK_F17; case KEY_NAME_INDEX_F18: return nsIDOMKeyEvent::DOM_VK_F18; case KEY_NAME_INDEX_F19: return nsIDOMKeyEvent::DOM_VK_F19; case KEY_NAME_INDEX_F20: return nsIDOMKeyEvent::DOM_VK_F20; case KEY_NAME_INDEX_F21: return nsIDOMKeyEvent::DOM_VK_F21; case KEY_NAME_INDEX_F22: return nsIDOMKeyEvent::DOM_VK_F22; case KEY_NAME_INDEX_F23: return nsIDOMKeyEvent::DOM_VK_F23; case KEY_NAME_INDEX_F24: return nsIDOMKeyEvent::DOM_VK_F24; case KEY_NAME_INDEX_NumLock: return nsIDOMKeyEvent::DOM_VK_NUM_LOCK; case KEY_NAME_INDEX_ScrollLock: return nsIDOMKeyEvent::DOM_VK_SCROLL_LOCK; case KEY_NAME_INDEX_AudioVolumeMute: return nsIDOMKeyEvent::DOM_VK_VOLUME_MUTE; case KEY_NAME_INDEX_AudioVolumeDown: return nsIDOMKeyEvent::DOM_VK_VOLUME_DOWN; case KEY_NAME_INDEX_AudioVolumeUp: return nsIDOMKeyEvent::DOM_VK_VOLUME_UP; case KEY_NAME_INDEX_Meta: return nsIDOMKeyEvent::DOM_VK_META; case KEY_NAME_INDEX_AltGraph: return nsIDOMKeyEvent::DOM_VK_ALTGR; case KEY_NAME_INDEX_Attn: return nsIDOMKeyEvent::DOM_VK_ATTN; case KEY_NAME_INDEX_CrSel: return nsIDOMKeyEvent::DOM_VK_CRSEL; case KEY_NAME_INDEX_ExSel: return nsIDOMKeyEvent::DOM_VK_EXSEL; case KEY_NAME_INDEX_EraseEof: return nsIDOMKeyEvent::DOM_VK_EREOF; case KEY_NAME_INDEX_Play: return nsIDOMKeyEvent::DOM_VK_PLAY; case KEY_NAME_INDEX_ZoomToggle: case KEY_NAME_INDEX_ZoomIn: case KEY_NAME_INDEX_ZoomOut: return nsIDOMKeyEvent::DOM_VK_ZOOM; default: return 0; } } /* static */ Modifier WidgetKeyboardEvent::GetModifierForKeyName(KeyNameIndex aKeyNameIndex) { switch (aKeyNameIndex) { case KEY_NAME_INDEX_Alt: return MODIFIER_ALT; case KEY_NAME_INDEX_AltGraph: return MODIFIER_ALTGRAPH; case KEY_NAME_INDEX_CapsLock: return MODIFIER_CAPSLOCK; case KEY_NAME_INDEX_Control: return MODIFIER_CONTROL; case KEY_NAME_INDEX_Fn: return MODIFIER_FN; case KEY_NAME_INDEX_FnLock: return MODIFIER_FNLOCK; // case KEY_NAME_INDEX_Hyper: case KEY_NAME_INDEX_Meta: return MODIFIER_META; case KEY_NAME_INDEX_NumLock: return MODIFIER_NUMLOCK; case KEY_NAME_INDEX_OS: return MODIFIER_OS; case KEY_NAME_INDEX_ScrollLock: return MODIFIER_SCROLLLOCK; case KEY_NAME_INDEX_Shift: return MODIFIER_SHIFT; // case KEY_NAME_INDEX_Super: case KEY_NAME_INDEX_Symbol: return MODIFIER_SYMBOL; case KEY_NAME_INDEX_SymbolLock: return MODIFIER_SYMBOLLOCK; default: return MODIFIER_NONE; } } /* static */ bool WidgetKeyboardEvent::IsLockableModifier(KeyNameIndex aKeyNameIndex) { switch (aKeyNameIndex) { case KEY_NAME_INDEX_CapsLock: case KEY_NAME_INDEX_FnLock: case KEY_NAME_INDEX_NumLock: case KEY_NAME_INDEX_ScrollLock: case KEY_NAME_INDEX_SymbolLock: return true; default: return false; } } } // namespace mozilla