diff options
Diffstat (limited to '')
-rw-r--r-- | widget/WidgetEventImpl.cpp | 1948 |
1 files changed, 1948 insertions, 0 deletions
diff --git a/widget/WidgetEventImpl.cpp b/widget/WidgetEventImpl.cpp new file mode 100644 index 0000000000..0f458f9339 --- /dev/null +++ b/widget/WidgetEventImpl.cpp @@ -0,0 +1,1948 @@ +/* -*- 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 "BasicEvents.h" +#include "ContentEvents.h" +#include "MiscEvents.h" +#include "MouseEvents.h" +#include "NativeKeyBindingsType.h" +#include "TextEventDispatcher.h" +#include "TextEvents.h" +#include "TouchEvents.h" + +#include "mozilla/EventStateManager.h" +#include "mozilla/InternalMutationEvent.h" +#include "mozilla/Maybe.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_mousewheel.h" +#include "mozilla/StaticPrefs_ui.h" +#include "mozilla/WritingModes.h" +#include "mozilla/dom/KeyboardEventBinding.h" +#include "mozilla/dom/WheelEventBinding.h" +#include "nsCommandParams.h" +#include "nsContentUtils.h" +#include "nsIContent.h" +#include "nsIDragSession.h" +#include "nsPrintfCString.h" + +#if defined(XP_WIN) +# include "windef.h" +# include "winnetwk.h" +# include "npapi.h" +# include "WinUtils.h" +#endif // #if defined (XP_WIN) + +#if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX) +# include "NativeKeyBindings.h" +#endif // #if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX) + +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 "USE_STRING"_ns; + } + nsAutoString keyName; + WidgetKeyboardEvent::GetDOMKeyName(aKeyNameIndex, keyName); + return NS_ConvertUTF16toUTF8(keyName); +} + +const nsCString ToString(CodeNameIndex aCodeNameIndex) { + if (aCodeNameIndex == CODE_NAME_INDEX_USE_STRING) { + return "USE_STRING"_ns; + } + nsAutoString codeName; + WidgetKeyboardEvent::GetDOMCodeName(aCodeNameIndex, codeName); + return NS_ConvertUTF16toUTF8(codeName); +} + +const char* ToChar(Command aCommand) { + if (aCommand == Command::DoNothing) { + return "CommandDoNothing"; + } + + switch (aCommand) { +#define NS_DEFINE_COMMAND(aName, aCommandStr) \ + case Command::aName: \ + return "Command::" #aName; +#define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam) \ + case Command::aName: \ + return "Command::" #aName; +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) \ + case Command::aName: \ + return "Command::" #aName; + +#include "mozilla/CommandList.h" + +#undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_WITH_PARAM +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND + + default: + return "illegal command value"; + } +} + +const nsCString GetDOMKeyCodeName(uint32_t aKeyCode) { + switch (aKeyCode) { +#define NS_DISALLOW_SAME_KEYCODE +#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ + case aDOMKeyCode: \ + return nsLiteralCString(#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<TextRangeType>(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<RawTextRangeType>(aTextRangeType); +} + +TextRangeType ToTextRangeType(RawTextRangeType aRawTextRangeType) { + MOZ_ASSERT(IsValidRawTextRangeValue(aRawTextRangeType)); + return static_cast<TextRangeType>(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; + } +} + +/****************************************************************************** + * non class method implementation + ******************************************************************************/ + +static nsTHashMap<nsDepCharHashKey, Command>* sCommandHashtable = nullptr; + +Command GetInternalCommand(const char* aCommandName, + const nsCommandParams* aCommandParams) { + if (!aCommandName) { + return Command::DoNothing; + } + + // Special cases for "cmd_align". It's mapped to multiple internal commands + // with additional param. Therefore, we cannot handle it with the hashtable. + if (!strcmp(aCommandName, "cmd_align")) { + if (!aCommandParams) { + // Note that if this is called by EditorCommand::IsCommandEnabled(), + // it cannot set aCommandParams. So, don't warn in this case even though + // this is illegal case for DoCommandParams(). + return Command::FormatJustify; + } + nsAutoCString cValue; + nsresult rv = aCommandParams->GetCString("state_attribute", cValue); + if (NS_FAILED(rv)) { + nsString value; // Avoid copying the string buffer with using nsString. + rv = aCommandParams->GetString("state_attribute", value); + if (NS_FAILED(rv)) { + return Command::FormatJustifyNone; + } + CopyUTF16toUTF8(value, cValue); + } + if (cValue.LowerCaseEqualsASCII("left")) { + return Command::FormatJustifyLeft; + } + if (cValue.LowerCaseEqualsASCII("right")) { + return Command::FormatJustifyRight; + } + if (cValue.LowerCaseEqualsASCII("center")) { + return Command::FormatJustifyCenter; + } + if (cValue.LowerCaseEqualsASCII("justify")) { + return Command::FormatJustifyFull; + } + if (cValue.IsEmpty()) { + return Command::FormatJustifyNone; + } + return Command::DoNothing; + } + + if (!sCommandHashtable) { + sCommandHashtable = new nsTHashMap<nsDepCharHashKey, Command>(); +#define NS_DEFINE_COMMAND(aName, aCommandStr) \ + sCommandHashtable->InsertOrUpdate(#aCommandStr, Command::aName); + +#define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam) + +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) + +#include "mozilla/CommandList.h" + +#undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_WITH_PARAM +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND + } + Command command = Command::DoNothing; + if (!sCommandHashtable->Get(aCommandName, &command)) { + return Command::DoNothing; + } + return command; +} + +/****************************************************************************** + * 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<WidgetEvent*>(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; +} + +/****************************************************************************** + * 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; + } +} + +/* static */ +bool WidgetEvent::IsKeyEventMessage(EventMessage aMessage) { + switch (aMessage) { + case eKeyDown: + case eKeyPress: + case eKeyUp: + 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; + } +} + +/****************************************************************************** + * 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 (IsCrossProcessForwardingStopped()) { + return false; + } + + if (mClass == eKeyboardEventClass || mClass == eWheelEventClass) { + return true; + } + + switch (mMessage) { + case eMouseDown: + case eMouseUp: + case eMouseMove: + case eMouseExploreByTouch: + 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::WillBeSentToRemoteProcess() const { + // This event won't be posted to remote process if it's already explicitly + // stopped. + if (IsCrossProcessForwardingStopped()) { + return false; + } + + // When mOriginalTarget is nullptr, this method shouldn't be used. + if (NS_WARN_IF(!mOriginalTarget)) { + return false; + } + + return EventStateManager::IsTopLevelRemoteTarget( + nsIContent::FromEventTarget(mOriginalTarget)); +} + +bool WidgetEvent::IsIMERelatedEvent() const { + return HasIMEEventMessage() || IsQueryContentEvent() || IsSelectionEvent(); +} + +bool WidgetEvent::IsUsingCoordinates() const { + const WidgetMouseEvent* mouseEvent = AsMouseEvent(); + if (mouseEvent) { + return !mouseEvent->IsContextMenuKeyEvent(); + } + return !HasKeyEventMessage() && !IsIMERelatedEvent() && + !IsContentCommandEvent(); +} + +bool WidgetEvent::IsTargetedAtFocusedWindow() const { + const WidgetMouseEvent* mouseEvent = AsMouseEvent(); + if (mouseEvent) { + return mouseEvent->IsContextMenuKeyEvent(); + } + return HasKeyEventMessage() || IsIMERelatedEvent() || IsContentCommandEvent(); +} + +bool WidgetEvent::IsTargetedAtFocusedContent() const { + const WidgetMouseEvent* mouseEvent = AsMouseEvent(); + if (mouseEvent) { + return mouseEvent->IsContextMenuKeyEvent(); + } + return HasKeyEventMessage() || IsIMERelatedEvent(); +} + +bool WidgetEvent::IsAllowedToDispatchDOMEvent() const { + switch (mClass) { + case eMouseEventClass: + if (mMessage == eMouseTouchDrag) { + return false; + } + [[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; +} + +bool WidgetEvent::IsBlockedForFingerprintingResistance() const { + switch (mClass) { + case eKeyboardEventClass: { + const WidgetKeyboardEvent* keyboardEvent = AsKeyboardEvent(); + + return (keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Alt || + keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Shift || + keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || + keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); + } + case ePointerEventClass: { + const WidgetPointerEvent* pointerEvent = AsPointerEvent(); + + // We suppress the pointer events if it is not primary for fingerprinting + // resistance. It is because of that we want to spoof any pointer event + // into a mouse pointer event and the mouse pointer event only has + // isPrimary as true. + return !pointerEvent->mIsPrimary; + } + default: + return false; + } +} + +/****************************************************************************** + * mozilla::WidgetEvent + * + * Misc methods. + ******************************************************************************/ + +static dom::EventTarget* GetTargetForDOMEvent(dom::EventTarget* 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(); +} + +void WidgetEvent::PreventDefault(bool aCalledByDefaultHandler, + nsIPrincipal* aPrincipal) { + if (mMessage == ePointerDown) { + if (aCalledByDefaultHandler) { + // Shouldn't prevent default on pointerdown by default handlers to stop + // firing legacy mouse events. Use MOZ_ASSERT to catch incorrect usages + // in debug builds. + MOZ_ASSERT(false); + return; + } + if (aPrincipal) { + nsAutoString addonId; + Unused << NS_WARN_IF(NS_FAILED(aPrincipal->GetAddonId(addonId))); + if (!addonId.IsEmpty()) { + // Ignore the case that it's called by a web extension. + return; + } + } + } + mFlags.PreventDefault(aCalledByDefaultHandler); +} + +bool WidgetEvent::IsUserAction() const { + if (!IsTrusted()) { + return false; + } + // FYI: eMouseScrollEventClass and ePointerEventClass represent + // user action but they are synthesized events. + switch (mClass) { + case eKeyboardEventClass: + case eCompositionEventClass: + case eMouseScrollEventClass: + case eWheelEventClass: + case eGestureNotifyEventClass: + case eSimpleGestureEventClass: + case eTouchEventClass: + case eCommandEventClass: + case eContentCommandEventClass: + return true; + case eMouseEventClass: + case eDragEventClass: + case ePointerEventClass: + return AsMouseEvent()->IsReal(); + default: + return false; + } +} + +/****************************************************************************** + * 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 (StaticPrefs::ui_key_accelKey()) { + case dom::KeyboardEvent_Binding::DOM_VK_META: + sAccelModifier = MODIFIER_META; + break; + case dom::KeyboardEvent_Binding::DOM_VK_WIN: + sAccelModifier = MODIFIER_OS; + break; + case dom::KeyboardEvent_Binding::DOM_VK_ALT: + sAccelModifier = MODIFIER_ALT; + break; + case dom::KeyboardEvent_Binding::DOM_VK_CONTROL: + sAccelModifier = MODIFIER_CONTROL; + break; + default: +#ifdef XP_MACOSX + sAccelModifier = MODIFIER_META; +#else + sAccelModifier = MODIFIER_CONTROL; +#endif + break; + } + } + return sAccelModifier; +} + +/****************************************************************************** + * mozilla::WidgetMouseEvent (MouseEvents.h) + ******************************************************************************/ + +/* static */ +bool WidgetMouseEvent::IsMiddleClickPasteEnabled() { + return Preferences::GetBool("middlemouse.paste", false); +} + +#ifdef DEBUG +void WidgetMouseEvent::AssertContextMenuEventButtonConsistency() const { + if (mMessage != eContextMenu) { + return; + } + + if (mContextMenuTrigger == eNormal) { + NS_WARNING_ASSERTION(mButton == MouseButton::eSecondary, + "eContextMenu events with eNormal trigger should use " + "secondary mouse button"); + } else { + NS_WARNING_ASSERTION(mButton == MouseButton::ePrimary, + "eContextMenu events with non-eNormal trigger should " + "use primary mouse button"); + } + + if (mContextMenuTrigger == eControlClick) { + NS_WARNING_ASSERTION(IsControl(), + "eContextMenu events with eControlClick trigger " + "should return true from IsControl()"); + } +} +#endif + +/****************************************************************************** + * mozilla::WidgetDragEvent (MouseEvents.h) + ******************************************************************************/ + +void WidgetDragEvent::InitDropEffectForTests() { + MOZ_ASSERT(mFlags.mIsSynthesizedForTests); + + nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession(); + if (NS_WARN_IF(!session)) { + return; + } + + uint32_t effectAllowed = session->GetEffectAllowedForTests(); + uint32_t desiredDropEffect = nsIDragService::DRAGDROP_ACTION_NONE; +#ifdef XP_MACOSX + if (IsAlt()) { + desiredDropEffect = IsMeta() ? nsIDragService::DRAGDROP_ACTION_LINK + : nsIDragService::DRAGDROP_ACTION_COPY; + } +#else + // On Linux, we know user's intention from API, but we should use + // same modifiers as Windows for tests because GNOME on Ubuntu use + // them and that makes each test simpler. + if (IsControl()) { + desiredDropEffect = IsShift() ? nsIDragService::DRAGDROP_ACTION_LINK + : nsIDragService::DRAGDROP_ACTION_COPY; + } else if (IsShift()) { + desiredDropEffect = nsIDragService::DRAGDROP_ACTION_MOVE; + } +#endif // #ifdef XP_MACOSX #else + // First, use modifier state for preferring action which is explicitly + // specified by the synthesizer. + if (!(desiredDropEffect &= effectAllowed)) { + // Otherwise, use an action which is allowed at starting the session. + desiredDropEffect = effectAllowed; + } + if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_MOVE) { + session->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE); + } else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_COPY) { + session->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY); + } else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_LINK) { + session->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK); + } else { + session->SetDragAction(nsIDragService::DRAGDROP_ACTION_NONE); + } +} + +/****************************************************************************** + * mozilla::WidgetWheelEvent (MouseEvents.h) + ******************************************************************************/ + +/* static */ +double WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, + bool aIsForVertical) { + if (!StaticPrefs::mousewheel_system_scroll_override_enabled()) { + return aDelta; + } + int32_t intFactor = + aIsForVertical + ? StaticPrefs::mousewheel_system_scroll_override_vertical_factor() + : StaticPrefs::mousewheel_system_scroll_override_horizontal_factor(); + // 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<double>(intFactor) / 100; + return aDelta * factor; +} + +double WidgetWheelEvent::OverriddenDeltaX() const { + if (!mAllowToOverrideSystemScrollSpeed || + mDeltaMode != dom::WheelEvent_Binding::DOM_DELTA_LINE || + mCustomizedByUserPrefs) { + return mDeltaX; + } + return ComputeOverriddenDelta(mDeltaX, false); +} + +double WidgetWheelEvent::OverriddenDeltaY() const { + if (!mAllowToOverrideSystemScrollSpeed || + mDeltaMode != dom::WheelEvent_Binding::DOM_DELTA_LINE || + mCustomizedByUserPrefs) { + 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( + const Maybe<WritingMode>& aWritingMode) { + // If this event is synthesized for tests, we don't need to retrieve the + // command via the main process. So, we don't need widget and can trust + // the event. + if (!mFlags.mIsSynthesizedForTests) { + // 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"); + } + + DebugOnly<bool> okIgnored = InitEditCommandsFor( + NativeKeyBindingsType::SingleLineEditor, aWritingMode); + NS_WARNING_ASSERTION(okIgnored, + "InitEditCommandsFor(NativeKeyBindingsType::" + "SingleLineEditor) failed, but ignored"); + okIgnored = + InitEditCommandsFor(NativeKeyBindingsType::MultiLineEditor, aWritingMode); + NS_WARNING_ASSERTION(okIgnored, + "InitEditCommandsFor(NativeKeyBindingsType::" + "MultiLineEditor) failed, but ignored"); + okIgnored = + InitEditCommandsFor(NativeKeyBindingsType::RichTextEditor, aWritingMode); + NS_WARNING_ASSERTION(okIgnored, + "InitEditCommandsFor(NativeKeyBindingsType::" + "RichTextEditor) failed, but ignored"); +} + +bool WidgetKeyboardEvent::InitEditCommandsFor( + NativeKeyBindingsType aType, const Maybe<WritingMode>& aWritingMode) { + bool& initialized = IsEditCommandsInitializedRef(aType); + if (initialized) { + return true; + } + nsTArray<CommandInt>& commands = EditCommandsRef(aType); + + // If this event is synthesized for tests, we shouldn't access customized + // shortcut settings of the environment. Therefore, we don't need to check + // whether `widget` is set or not. And we can treat synthesized events are + // always trusted. + if (mFlags.mIsSynthesizedForTests) { + MOZ_DIAGNOSTIC_ASSERT(IsTrusted()); +#if defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX) + // TODO: We should implement `NativeKeyBindings` for Windows and Android + // too in bug 1301497 for getting rid of the #if. + widget::NativeKeyBindings::GetEditCommandsForTests(aType, *this, + aWritingMode, commands); +#endif + initialized = true; + return true; + } + + if (NS_WARN_IF(!mWidget) || NS_WARN_IF(!IsTrusted())) { + return false; + } + // `nsIWidget::GetEditCommands()` will retrieve `WritingMode` at selection + // again, but it should be almost zero-cost since `TextEventDispatcher` + // caches the value. + nsCOMPtr<nsIWidget> widget = mWidget; + initialized = widget->GetEditCommands(aType, *this, commands); + return initialized; +} + +bool WidgetKeyboardEvent::ExecuteEditCommands(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; + } + + if (!IsEditCommandsInitializedRef(aType)) { + Maybe<WritingMode> writingMode; + if (RefPtr<widget::TextEventDispatcher> textEventDispatcher = + mWidget->GetTextEventDispatcher()) { + writingMode = textEventDispatcher->MaybeQueryWritingModeAtSelection(); + } + if (NS_WARN_IF(!InitEditCommandsFor(aType, writingMode))) { + return false; + } + } + + const nsTArray<CommandInt>& commands = EditCommandsRef(aType); + if (commands.IsEmpty()) { + return false; + } + + for (CommandInt command : commands) { + aCallback(static_cast<Command>(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<char16_t>(aChar1)) == + ToLowerCase(static_cast<char16_t>(aChar2))); +} + +static bool IsCaseChangeableChar(uint32_t aChar) { + return IS_IN_BMP(aChar) && ToLowerCase(static_cast<char16_t>(aChar)) != + ToUpperCase(static_cast<char16_t>(aChar)); +} + +void WidgetKeyboardEvent::GetShortcutKeyCandidates( + ShortcutKeyCandidateArray& aCandidates) const { + 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<uint32_t>& aCandidates) const { + 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],... + uint32_t pseudoCharCode = PseudoCharCode(); + if (pseudoCharCode) { + uint32_t ch = pseudoCharCode; + if (IS_IN_BMP(ch)) { + ch = ToLowerCase(static_cast<char16_t>(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<char16_t>(ch[j])); + } + // Don't append the charcode 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 && pseudoCharCode != ' ') { + aCandidates.AppendElement(' '); + } +} + +// mask values for ui.key.chromeAccess and ui.key.contentAccess +#define NS_MODIFIER_SHIFT 1 +#define NS_MODIFIER_CONTROL 2 +#define NS_MODIFIER_ALT 4 +#define NS_MODIFIER_META 8 +#define NS_MODIFIER_OS 16 + +static Modifiers PrefFlagsToModifiers(int32_t aPrefFlags) { + Modifiers result = 0; + if (aPrefFlags & NS_MODIFIER_SHIFT) { + result |= MODIFIER_SHIFT; + } + if (aPrefFlags & NS_MODIFIER_CONTROL) { + result |= MODIFIER_CONTROL; + } + if (aPrefFlags & NS_MODIFIER_ALT) { + result |= MODIFIER_ALT; + } + if (aPrefFlags & NS_MODIFIER_META) { + result |= MODIFIER_META; + } + if (aPrefFlags & NS_MODIFIER_OS) { + result |= MODIFIER_OS; + } + return result; +} + +bool WidgetKeyboardEvent::ModifiersMatchWithAccessKey( + AccessKeyType aType) const { + if (!ModifiersForAccessKeyMatching()) { + return false; + } + return ModifiersForAccessKeyMatching() == AccessKeyModifiers(aType); +} + +Modifiers WidgetKeyboardEvent::ModifiersForAccessKeyMatching() const { + static const Modifiers kModifierMask = MODIFIER_SHIFT | MODIFIER_CONTROL | + MODIFIER_ALT | MODIFIER_META | + MODIFIER_OS; + return mModifiers & kModifierMask; +} + +/* static */ +Modifiers WidgetKeyboardEvent::AccessKeyModifiers(AccessKeyType aType) { + switch (StaticPrefs::ui_key_generalAccessKey()) { + case -1: + break; // use the individual prefs + case NS_VK_SHIFT: + return MODIFIER_SHIFT; + case NS_VK_CONTROL: + return MODIFIER_CONTROL; + case NS_VK_ALT: + return MODIFIER_ALT; + case NS_VK_META: + return MODIFIER_META; + case NS_VK_WIN: + return MODIFIER_OS; + default: + return MODIFIER_NONE; + } + + switch (aType) { + case AccessKeyType::eChrome: + return PrefFlagsToModifiers(StaticPrefs::ui_key_chromeAccess()); + case AccessKeyType::eContent: + return PrefFlagsToModifiers(StaticPrefs::ui_key_contentAccess()); + default: + return MODIFIER_NONE; + } +} + +/* static */ +void WidgetKeyboardEvent::Shutdown() { + delete sKeyNameIndexHashtable; + sKeyNameIndexHashtable = nullptr; + delete sCodeNameIndexHashtable; + sCodeNameIndexHashtable = nullptr; + // Although sCommandHashtable is not a member of WidgetKeyboardEvent, but + // let's delete it here since we need to do it at same time. + delete sCommandHashtable; + sCommandHashtable = nullptr; +} + +/* static */ +void WidgetKeyboardEvent::GetDOMKeyName(KeyNameIndex aKeyNameIndex, + nsAString& aKeyName) { + if (aKeyNameIndex >= KEY_NAME_INDEX_USE_STRING) { + aKeyName.Truncate(); + return; + } + + MOZ_RELEASE_ASSERT( + static_cast<size_t>(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<size_t>(aCodeNameIndex) < ArrayLength(kCodeNames), + "Illegal physical code enumeration value"); + + // Generate some continuous runs of codes, rather than looking them up. + if (aCodeNameIndex >= CODE_NAME_INDEX_KeyA && + aCodeNameIndex <= CODE_NAME_INDEX_KeyZ) { + uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_KeyA; + aCodeName.AssignLiteral(u"Key"); + aCodeName.Append(u'A' + index); + return; + } + if (aCodeNameIndex >= CODE_NAME_INDEX_Digit0 && + aCodeNameIndex <= CODE_NAME_INDEX_Digit9) { + uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_Digit0; + aCodeName.AssignLiteral(u"Digit"); + aCodeName.AppendInt(index); + return; + } + if (aCodeNameIndex >= CODE_NAME_INDEX_Numpad0 && + aCodeNameIndex <= CODE_NAME_INDEX_Numpad9) { + uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_Numpad0; + aCodeName.AssignLiteral(u"Numpad"); + aCodeName.AppendInt(index); + return; + } + if (aCodeNameIndex >= CODE_NAME_INDEX_F1 && + aCodeNameIndex <= CODE_NAME_INDEX_F24) { + uint32_t index = aCodeNameIndex - CODE_NAME_INDEX_F1; + aCodeName.Assign(u'F'); + aCodeName.AppendInt(index + 1); + return; + } + + 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->InsertOrUpdate(nsDependentString(kKeyNames[i]), + static_cast<KeyNameIndex>(i)); + } + } + return sKeyNameIndexHashtable->MaybeGet(aKeyValue).valueOr( + KEY_NAME_INDEX_USE_STRING); +} + +/* static */ +CodeNameIndex WidgetKeyboardEvent::GetCodeNameIndex( + const nsAString& aCodeValue) { + if (!sCodeNameIndexHashtable) { + sCodeNameIndexHashtable = + new CodeNameIndexHashtable(ArrayLength(kCodeNames)); + for (size_t i = 0; i < ArrayLength(kCodeNames); i++) { + sCodeNameIndexHashtable->InsertOrUpdate(nsDependentString(kCodeNames[i]), + static_cast<CodeNameIndex>(i)); + } + } + return sCodeNameIndexHashtable->MaybeGet(aCodeValue) + .valueOr(CODE_NAME_INDEX_USE_STRING); +} + +/* static */ +uint32_t WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey( + CodeNameIndex aCodeNameIndex) { + switch (aCodeNameIndex) { + case CODE_NAME_INDEX_Semicolon: // VK_OEM_1 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON; + case CODE_NAME_INDEX_Equal: // VK_OEM_PLUS on Windows + return dom::KeyboardEvent_Binding::DOM_VK_EQUALS; + case CODE_NAME_INDEX_Comma: // VK_OEM_COMMA on Windows + return dom::KeyboardEvent_Binding::DOM_VK_COMMA; + case CODE_NAME_INDEX_Minus: // VK_OEM_MINUS on Windows + return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS; + case CODE_NAME_INDEX_Period: // VK_OEM_PERIOD on Windows + return dom::KeyboardEvent_Binding::DOM_VK_PERIOD; + case CODE_NAME_INDEX_Slash: // VK_OEM_2 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_SLASH; + case CODE_NAME_INDEX_Backquote: // VK_OEM_3 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE; + case CODE_NAME_INDEX_BracketLeft: // VK_OEM_4 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET; + case CODE_NAME_INDEX_Backslash: // VK_OEM_5 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH; + case CODE_NAME_INDEX_BracketRight: // VK_OEM_6 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET; + case CODE_NAME_INDEX_Quote: // VK_OEM_7 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_QUOTE; + case CODE_NAME_INDEX_IntlBackslash: // VK_OEM_5 on Windows (ABNT, etc) + case CODE_NAME_INDEX_IntlYen: // VK_OEM_5 on Windows (JIS) + case CODE_NAME_INDEX_IntlRo: // VK_OEM_102 on Windows + return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH; + default: + return 0; + } +} + +/* static */ const char* WidgetKeyboardEvent::GetCommandStr(Command aCommand) { +#define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr +#define NS_DEFINE_COMMAND_WITH_PARAM(aName, aCommandStr, aParam) , #aCommandStr +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) , "" + static const char* const kCommands[] = { + "" // DoNothing +#include "mozilla/CommandList.h" + }; +#undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_WITH_PARAM +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND + + MOZ_RELEASE_ASSERT(static_cast<size_t>(aCommand) < ArrayLength(kCommands), + "Illegal command enumeration value"); + return kCommands[static_cast<CommandInt>(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 dom::KeyboardEvent_Binding::DOM_VK_CANCEL; + case KEY_NAME_INDEX_Help: + return dom::KeyboardEvent_Binding::DOM_VK_HELP; + case KEY_NAME_INDEX_Backspace: + return dom::KeyboardEvent_Binding::DOM_VK_BACK_SPACE; + case KEY_NAME_INDEX_Tab: + return dom::KeyboardEvent_Binding::DOM_VK_TAB; + case KEY_NAME_INDEX_Clear: + return dom::KeyboardEvent_Binding::DOM_VK_CLEAR; + case KEY_NAME_INDEX_Enter: + return dom::KeyboardEvent_Binding::DOM_VK_RETURN; + case KEY_NAME_INDEX_Shift: + return dom::KeyboardEvent_Binding::DOM_VK_SHIFT; + case KEY_NAME_INDEX_Control: + return dom::KeyboardEvent_Binding::DOM_VK_CONTROL; + case KEY_NAME_INDEX_Alt: + return dom::KeyboardEvent_Binding::DOM_VK_ALT; + case KEY_NAME_INDEX_Pause: + return dom::KeyboardEvent_Binding::DOM_VK_PAUSE; + case KEY_NAME_INDEX_CapsLock: + return dom::KeyboardEvent_Binding::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 dom::KeyboardEvent_Binding::DOM_VK_KANA; + case KEY_NAME_INDEX_HangulMode: + return dom::KeyboardEvent_Binding::DOM_VK_HANGUL; + case KEY_NAME_INDEX_Eisu: + return dom::KeyboardEvent_Binding::DOM_VK_EISU; + case KEY_NAME_INDEX_JunjaMode: + return dom::KeyboardEvent_Binding::DOM_VK_JUNJA; + case KEY_NAME_INDEX_FinalMode: + return dom::KeyboardEvent_Binding::DOM_VK_FINAL; + case KEY_NAME_INDEX_HanjaMode: + return dom::KeyboardEvent_Binding::DOM_VK_HANJA; + case KEY_NAME_INDEX_KanjiMode: + return dom::KeyboardEvent_Binding::DOM_VK_KANJI; + case KEY_NAME_INDEX_Escape: + return dom::KeyboardEvent_Binding::DOM_VK_ESCAPE; + case KEY_NAME_INDEX_Convert: + return dom::KeyboardEvent_Binding::DOM_VK_CONVERT; + case KEY_NAME_INDEX_NonConvert: + return dom::KeyboardEvent_Binding::DOM_VK_NONCONVERT; + case KEY_NAME_INDEX_Accept: + return dom::KeyboardEvent_Binding::DOM_VK_ACCEPT; + case KEY_NAME_INDEX_ModeChange: + return dom::KeyboardEvent_Binding::DOM_VK_MODECHANGE; + case KEY_NAME_INDEX_PageUp: + return dom::KeyboardEvent_Binding::DOM_VK_PAGE_UP; + case KEY_NAME_INDEX_PageDown: + return dom::KeyboardEvent_Binding::DOM_VK_PAGE_DOWN; + case KEY_NAME_INDEX_End: + return dom::KeyboardEvent_Binding::DOM_VK_END; + case KEY_NAME_INDEX_Home: + return dom::KeyboardEvent_Binding::DOM_VK_HOME; + case KEY_NAME_INDEX_ArrowLeft: + return dom::KeyboardEvent_Binding::DOM_VK_LEFT; + case KEY_NAME_INDEX_ArrowUp: + return dom::KeyboardEvent_Binding::DOM_VK_UP; + case KEY_NAME_INDEX_ArrowRight: + return dom::KeyboardEvent_Binding::DOM_VK_RIGHT; + case KEY_NAME_INDEX_ArrowDown: + return dom::KeyboardEvent_Binding::DOM_VK_DOWN; + case KEY_NAME_INDEX_Select: + return dom::KeyboardEvent_Binding::DOM_VK_SELECT; + case KEY_NAME_INDEX_Print: + return dom::KeyboardEvent_Binding::DOM_VK_PRINT; + case KEY_NAME_INDEX_Execute: + return dom::KeyboardEvent_Binding::DOM_VK_EXECUTE; + case KEY_NAME_INDEX_PrintScreen: + return dom::KeyboardEvent_Binding::DOM_VK_PRINTSCREEN; + case KEY_NAME_INDEX_Insert: + return dom::KeyboardEvent_Binding::DOM_VK_INSERT; + case KEY_NAME_INDEX_Delete: + return dom::KeyboardEvent_Binding::DOM_VK_DELETE; + case KEY_NAME_INDEX_OS: + // case KEY_NAME_INDEX_Super: + // case KEY_NAME_INDEX_Hyper: + return dom::KeyboardEvent_Binding::DOM_VK_WIN; + case KEY_NAME_INDEX_ContextMenu: + return dom::KeyboardEvent_Binding::DOM_VK_CONTEXT_MENU; + case KEY_NAME_INDEX_Standby: + return dom::KeyboardEvent_Binding::DOM_VK_SLEEP; + case KEY_NAME_INDEX_F1: + return dom::KeyboardEvent_Binding::DOM_VK_F1; + case KEY_NAME_INDEX_F2: + return dom::KeyboardEvent_Binding::DOM_VK_F2; + case KEY_NAME_INDEX_F3: + return dom::KeyboardEvent_Binding::DOM_VK_F3; + case KEY_NAME_INDEX_F4: + return dom::KeyboardEvent_Binding::DOM_VK_F4; + case KEY_NAME_INDEX_F5: + return dom::KeyboardEvent_Binding::DOM_VK_F5; + case KEY_NAME_INDEX_F6: + return dom::KeyboardEvent_Binding::DOM_VK_F6; + case KEY_NAME_INDEX_F7: + return dom::KeyboardEvent_Binding::DOM_VK_F7; + case KEY_NAME_INDEX_F8: + return dom::KeyboardEvent_Binding::DOM_VK_F8; + case KEY_NAME_INDEX_F9: + return dom::KeyboardEvent_Binding::DOM_VK_F9; + case KEY_NAME_INDEX_F10: + return dom::KeyboardEvent_Binding::DOM_VK_F10; + case KEY_NAME_INDEX_F11: + return dom::KeyboardEvent_Binding::DOM_VK_F11; + case KEY_NAME_INDEX_F12: + return dom::KeyboardEvent_Binding::DOM_VK_F12; + case KEY_NAME_INDEX_F13: + return dom::KeyboardEvent_Binding::DOM_VK_F13; + case KEY_NAME_INDEX_F14: + return dom::KeyboardEvent_Binding::DOM_VK_F14; + case KEY_NAME_INDEX_F15: + return dom::KeyboardEvent_Binding::DOM_VK_F15; + case KEY_NAME_INDEX_F16: + return dom::KeyboardEvent_Binding::DOM_VK_F16; + case KEY_NAME_INDEX_F17: + return dom::KeyboardEvent_Binding::DOM_VK_F17; + case KEY_NAME_INDEX_F18: + return dom::KeyboardEvent_Binding::DOM_VK_F18; + case KEY_NAME_INDEX_F19: + return dom::KeyboardEvent_Binding::DOM_VK_F19; + case KEY_NAME_INDEX_F20: + return dom::KeyboardEvent_Binding::DOM_VK_F20; + case KEY_NAME_INDEX_F21: + return dom::KeyboardEvent_Binding::DOM_VK_F21; + case KEY_NAME_INDEX_F22: + return dom::KeyboardEvent_Binding::DOM_VK_F22; + case KEY_NAME_INDEX_F23: + return dom::KeyboardEvent_Binding::DOM_VK_F23; + case KEY_NAME_INDEX_F24: + return dom::KeyboardEvent_Binding::DOM_VK_F24; + case KEY_NAME_INDEX_NumLock: + return dom::KeyboardEvent_Binding::DOM_VK_NUM_LOCK; + case KEY_NAME_INDEX_ScrollLock: + return dom::KeyboardEvent_Binding::DOM_VK_SCROLL_LOCK; + case KEY_NAME_INDEX_AudioVolumeMute: + return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_MUTE; + case KEY_NAME_INDEX_AudioVolumeDown: + return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_DOWN; + case KEY_NAME_INDEX_AudioVolumeUp: + return dom::KeyboardEvent_Binding::DOM_VK_VOLUME_UP; + case KEY_NAME_INDEX_Meta: + return dom::KeyboardEvent_Binding::DOM_VK_META; + case KEY_NAME_INDEX_AltGraph: + return dom::KeyboardEvent_Binding::DOM_VK_ALTGR; + case KEY_NAME_INDEX_Process: + return dom::KeyboardEvent_Binding::DOM_VK_PROCESSKEY; + case KEY_NAME_INDEX_Attn: + return dom::KeyboardEvent_Binding::DOM_VK_ATTN; + case KEY_NAME_INDEX_CrSel: + return dom::KeyboardEvent_Binding::DOM_VK_CRSEL; + case KEY_NAME_INDEX_ExSel: + return dom::KeyboardEvent_Binding::DOM_VK_EXSEL; + case KEY_NAME_INDEX_EraseEof: + return dom::KeyboardEvent_Binding::DOM_VK_EREOF; + case KEY_NAME_INDEX_Play: + return dom::KeyboardEvent_Binding::DOM_VK_PLAY; + case KEY_NAME_INDEX_ZoomToggle: + case KEY_NAME_INDEX_ZoomIn: + case KEY_NAME_INDEX_ZoomOut: + return dom::KeyboardEvent_Binding::DOM_VK_ZOOM; + default: + return 0; + } +} + +/* static */ +CodeNameIndex WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex( + KeyNameIndex aKeyNameIndex, const Maybe<uint32_t>& aLocation) { + if (aLocation.isSome() && + aLocation.value() == + dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) { + // On macOS, NumLock is not supported. Therefore, this handles + // control key values except "Enter" only on non-macOS platforms. + switch (aKeyNameIndex) { +#ifndef XP_MACOSX + case KEY_NAME_INDEX_Insert: + return CODE_NAME_INDEX_Numpad0; + case KEY_NAME_INDEX_End: + return CODE_NAME_INDEX_Numpad1; + case KEY_NAME_INDEX_ArrowDown: + return CODE_NAME_INDEX_Numpad2; + case KEY_NAME_INDEX_PageDown: + return CODE_NAME_INDEX_Numpad3; + case KEY_NAME_INDEX_ArrowLeft: + return CODE_NAME_INDEX_Numpad4; + case KEY_NAME_INDEX_Clear: + // FYI: "Clear" on macOS should be DOM_KEY_LOCATION_STANDARD. + return CODE_NAME_INDEX_Numpad5; + case KEY_NAME_INDEX_ArrowRight: + return CODE_NAME_INDEX_Numpad6; + case KEY_NAME_INDEX_Home: + return CODE_NAME_INDEX_Numpad7; + case KEY_NAME_INDEX_ArrowUp: + return CODE_NAME_INDEX_Numpad8; + case KEY_NAME_INDEX_PageUp: + return CODE_NAME_INDEX_Numpad9; + case KEY_NAME_INDEX_Delete: + return CODE_NAME_INDEX_NumpadDecimal; +#endif // #ifndef XP_MACOSX + case KEY_NAME_INDEX_Enter: + return CODE_NAME_INDEX_NumpadEnter; + default: + return CODE_NAME_INDEX_UNKNOWN; + } + } + + if (WidgetKeyboardEvent::IsLeftOrRightModiferKeyNameIndex(aKeyNameIndex)) { + if (aLocation.isSome() && + (aLocation.value() != + dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_LEFT && + aLocation.value() != + dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT)) { + return CODE_NAME_INDEX_UNKNOWN; + } + bool isRight = + aLocation.isSome() && + aLocation.value() == dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT; + switch (aKeyNameIndex) { + case KEY_NAME_INDEX_Alt: + return isRight ? CODE_NAME_INDEX_AltRight : CODE_NAME_INDEX_AltLeft; + case KEY_NAME_INDEX_Control: + return isRight ? CODE_NAME_INDEX_ControlRight + : CODE_NAME_INDEX_ControlLeft; + case KEY_NAME_INDEX_Shift: + return isRight ? CODE_NAME_INDEX_ShiftRight : CODE_NAME_INDEX_ShiftLeft; +#if defined(XP_WIN) + case KEY_NAME_INDEX_Meta: + return CODE_NAME_INDEX_UNKNOWN; + case KEY_NAME_INDEX_OS: // win key. + return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft; +#elif defined(XP_MACOSX) || defined(ANDROID) + case KEY_NAME_INDEX_Meta: // command key. + return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft; + case KEY_NAME_INDEX_OS: + return CODE_NAME_INDEX_UNKNOWN; +#else + case KEY_NAME_INDEX_Meta: // Alt + Shift. + return isRight ? CODE_NAME_INDEX_AltRight : CODE_NAME_INDEX_AltLeft; + case KEY_NAME_INDEX_OS: // Super/Hyper key. + return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft; +#endif + default: + return CODE_NAME_INDEX_UNKNOWN; + } + } + + if (aLocation.isSome() && + aLocation.value() != + dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) { + return CODE_NAME_INDEX_UNKNOWN; + } + + switch (aKeyNameIndex) { + // Standard section: + case KEY_NAME_INDEX_Escape: + return CODE_NAME_INDEX_Escape; + case KEY_NAME_INDEX_Tab: + return CODE_NAME_INDEX_Tab; + case KEY_NAME_INDEX_CapsLock: + return CODE_NAME_INDEX_CapsLock; + case KEY_NAME_INDEX_ContextMenu: + return CODE_NAME_INDEX_ContextMenu; + case KEY_NAME_INDEX_Backspace: + return CODE_NAME_INDEX_Backspace; + case KEY_NAME_INDEX_Enter: + return CODE_NAME_INDEX_Enter; +#ifdef XP_MACOSX + // Although, macOS does not fire native key event of "Fn" key, we support + // Fn key event if it's sent by other apps directly. + case KEY_NAME_INDEX_Fn: + return CODE_NAME_INDEX_Fn; +#endif // #ifdef + + // Arrow Pad section: + case KEY_NAME_INDEX_ArrowLeft: + return CODE_NAME_INDEX_ArrowLeft; + case KEY_NAME_INDEX_ArrowUp: + return CODE_NAME_INDEX_ArrowUp; + case KEY_NAME_INDEX_ArrowDown: + return CODE_NAME_INDEX_ArrowDown; + case KEY_NAME_INDEX_ArrowRight: + return CODE_NAME_INDEX_ArrowRight; + + // Control Pad section: +#ifndef XP_MACOSX + case KEY_NAME_INDEX_Insert: + return CODE_NAME_INDEX_Insert; +#else + case KEY_NAME_INDEX_Help: + return CODE_NAME_INDEX_Help; +#endif // #ifndef XP_MACOSX #else + case KEY_NAME_INDEX_Delete: + return CODE_NAME_INDEX_Delete; + case KEY_NAME_INDEX_Home: + return CODE_NAME_INDEX_Home; + case KEY_NAME_INDEX_End: + return CODE_NAME_INDEX_End; + case KEY_NAME_INDEX_PageUp: + return CODE_NAME_INDEX_PageUp; + case KEY_NAME_INDEX_PageDown: + return CODE_NAME_INDEX_PageDown; + + // Function keys: + case KEY_NAME_INDEX_F1: + return CODE_NAME_INDEX_F1; + case KEY_NAME_INDEX_F2: + return CODE_NAME_INDEX_F2; + case KEY_NAME_INDEX_F3: + return CODE_NAME_INDEX_F3; + case KEY_NAME_INDEX_F4: + return CODE_NAME_INDEX_F4; + case KEY_NAME_INDEX_F5: + return CODE_NAME_INDEX_F5; + case KEY_NAME_INDEX_F6: + return CODE_NAME_INDEX_F6; + case KEY_NAME_INDEX_F7: + return CODE_NAME_INDEX_F7; + case KEY_NAME_INDEX_F8: + return CODE_NAME_INDEX_F8; + case KEY_NAME_INDEX_F9: + return CODE_NAME_INDEX_F9; + case KEY_NAME_INDEX_F10: + return CODE_NAME_INDEX_F10; + case KEY_NAME_INDEX_F11: + return CODE_NAME_INDEX_F11; + case KEY_NAME_INDEX_F12: + return CODE_NAME_INDEX_F12; + case KEY_NAME_INDEX_F13: + return CODE_NAME_INDEX_F13; + case KEY_NAME_INDEX_F14: + return CODE_NAME_INDEX_F14; + case KEY_NAME_INDEX_F15: + return CODE_NAME_INDEX_F15; + case KEY_NAME_INDEX_F16: + return CODE_NAME_INDEX_F16; + case KEY_NAME_INDEX_F17: + return CODE_NAME_INDEX_F17; + case KEY_NAME_INDEX_F18: + return CODE_NAME_INDEX_F18; + case KEY_NAME_INDEX_F19: + return CODE_NAME_INDEX_F19; + case KEY_NAME_INDEX_F20: + return CODE_NAME_INDEX_F20; +#ifndef XP_MACOSX + case KEY_NAME_INDEX_F21: + return CODE_NAME_INDEX_F21; + case KEY_NAME_INDEX_F22: + return CODE_NAME_INDEX_F22; + case KEY_NAME_INDEX_F23: + return CODE_NAME_INDEX_F23; + case KEY_NAME_INDEX_F24: + return CODE_NAME_INDEX_F24; + case KEY_NAME_INDEX_Pause: + return CODE_NAME_INDEX_Pause; + case KEY_NAME_INDEX_PrintScreen: + return CODE_NAME_INDEX_PrintScreen; + case KEY_NAME_INDEX_ScrollLock: + return CODE_NAME_INDEX_ScrollLock; +#endif // #ifndef XP_MACOSX + + // NumLock key: +#ifndef XP_MACOSX + case KEY_NAME_INDEX_NumLock: + return CODE_NAME_INDEX_NumLock; +#else + case KEY_NAME_INDEX_Clear: + return CODE_NAME_INDEX_NumLock; +#endif // #ifndef XP_MACOSX #else + + // Media keys: + case KEY_NAME_INDEX_AudioVolumeDown: + return CODE_NAME_INDEX_VolumeDown; + case KEY_NAME_INDEX_AudioVolumeMute: + return CODE_NAME_INDEX_VolumeMute; + case KEY_NAME_INDEX_AudioVolumeUp: + return CODE_NAME_INDEX_VolumeUp; +#ifndef XP_MACOSX + case KEY_NAME_INDEX_BrowserBack: + return CODE_NAME_INDEX_BrowserBack; + case KEY_NAME_INDEX_BrowserFavorites: + return CODE_NAME_INDEX_BrowserFavorites; + case KEY_NAME_INDEX_BrowserForward: + return CODE_NAME_INDEX_BrowserForward; + case KEY_NAME_INDEX_BrowserRefresh: + return CODE_NAME_INDEX_BrowserRefresh; + case KEY_NAME_INDEX_BrowserSearch: + return CODE_NAME_INDEX_BrowserSearch; + case KEY_NAME_INDEX_BrowserStop: + return CODE_NAME_INDEX_BrowserStop; + case KEY_NAME_INDEX_MediaPlayPause: + return CODE_NAME_INDEX_MediaPlayPause; + case KEY_NAME_INDEX_MediaStop: + return CODE_NAME_INDEX_MediaStop; + case KEY_NAME_INDEX_MediaTrackNext: + return CODE_NAME_INDEX_MediaTrackNext; + case KEY_NAME_INDEX_MediaTrackPrevious: + return CODE_NAME_INDEX_MediaTrackPrevious; + case KEY_NAME_INDEX_LaunchApplication1: + return CODE_NAME_INDEX_LaunchApp1; +#endif // #ifndef XP_MACOSX + + // Only Windows and GTK supports the following multimedia keys. +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + case KEY_NAME_INDEX_BrowserHome: + return CODE_NAME_INDEX_BrowserHome; + case KEY_NAME_INDEX_LaunchApplication2: + return CODE_NAME_INDEX_LaunchApp2; +#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + + // Only GTK and Android supports the following multimedia keys. +#if defined(MOZ_WIDGET_GTK) || defined(ANDROID) + case KEY_NAME_INDEX_Eject: + return CODE_NAME_INDEX_Eject; + case KEY_NAME_INDEX_WakeUp: + return CODE_NAME_INDEX_WakeUp; +#endif // #if defined(MOZ_WIDGET_GTK) || defined(ANDROID) + + // Only Windows does not support Help key (and macOS handled above). +#if !defined(XP_WIN) && !defined(XP_MACOSX) + case KEY_NAME_INDEX_Help: + return CODE_NAME_INDEX_Help; +#endif // #if !defined(XP_WIN) && !defined(XP_MACOSX) + + // IME specific keys: +#ifdef XP_WIN + case KEY_NAME_INDEX_Convert: + return CODE_NAME_INDEX_Convert; + case KEY_NAME_INDEX_NonConvert: + return CODE_NAME_INDEX_NonConvert; + case KEY_NAME_INDEX_Alphanumeric: + return CODE_NAME_INDEX_CapsLock; + case KEY_NAME_INDEX_KanaMode: + case KEY_NAME_INDEX_Romaji: + case KEY_NAME_INDEX_Katakana: + case KEY_NAME_INDEX_Hiragana: + return CODE_NAME_INDEX_KanaMode; + case KEY_NAME_INDEX_Hankaku: + case KEY_NAME_INDEX_Zenkaku: + case KEY_NAME_INDEX_KanjiMode: + return CODE_NAME_INDEX_Backquote; + case KEY_NAME_INDEX_HanjaMode: + return CODE_NAME_INDEX_Lang2; + case KEY_NAME_INDEX_HangulMode: + return CODE_NAME_INDEX_Lang1; +#endif // #ifdef XP_WIN + +#ifdef MOZ_WIDGET_GTK + case KEY_NAME_INDEX_Convert: + return CODE_NAME_INDEX_Convert; + case KEY_NAME_INDEX_NonConvert: + return CODE_NAME_INDEX_NonConvert; + case KEY_NAME_INDEX_Alphanumeric: + return CODE_NAME_INDEX_CapsLock; + case KEY_NAME_INDEX_HiraganaKatakana: + return CODE_NAME_INDEX_KanaMode; + case KEY_NAME_INDEX_ZenkakuHankaku: + return CODE_NAME_INDEX_Backquote; +#endif // #ifdef MOZ_WIDGET_GTK + +#ifdef ANDROID + case KEY_NAME_INDEX_Convert: + return CODE_NAME_INDEX_Convert; + case KEY_NAME_INDEX_NonConvert: + return CODE_NAME_INDEX_NonConvert; + case KEY_NAME_INDEX_HiraganaKatakana: + return CODE_NAME_INDEX_KanaMode; + case KEY_NAME_INDEX_ZenkakuHankaku: + return CODE_NAME_INDEX_Backquote; + case KEY_NAME_INDEX_Eisu: + return CODE_NAME_INDEX_Lang2; + case KEY_NAME_INDEX_KanjiMode: + return CODE_NAME_INDEX_Lang1; +#endif // #ifdef ANDROID + +#ifdef XP_MACOSX + case KEY_NAME_INDEX_Eisu: + return CODE_NAME_INDEX_Lang2; + case KEY_NAME_INDEX_KanjiMode: + return CODE_NAME_INDEX_Lang1; +#endif // #ifdef XP_MACOSX + + default: + return CODE_NAME_INDEX_UNKNOWN; + } +} + +/* 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; + } +} + +/****************************************************************************** + * mozilla::InternalEditorInputEvent (TextEvents.h) + ******************************************************************************/ + +#define NS_DEFINE_INPUTTYPE(aCPPName, aDOMName) (u"" aDOMName), +const char16_t* const InternalEditorInputEvent::kInputTypeNames[] = { +#include "mozilla/InputTypeList.h" +}; +#undef NS_DEFINE_INPUTTYPE + +InternalEditorInputEvent::InputTypeHashtable* + InternalEditorInputEvent::sInputTypeHashtable = nullptr; + +/* static */ +void InternalEditorInputEvent::Shutdown() { + delete sInputTypeHashtable; + sInputTypeHashtable = nullptr; +} + +/* static */ +void InternalEditorInputEvent::GetDOMInputTypeName(EditorInputType aInputType, + nsAString& aInputTypeName) { + if (static_cast<size_t>(aInputType) >= + static_cast<size_t>(EditorInputType::eUnknown)) { + aInputTypeName.Truncate(); + return; + } + + MOZ_RELEASE_ASSERT( + static_cast<size_t>(aInputType) < ArrayLength(kInputTypeNames), + "Illegal input type enumeration value"); + aInputTypeName.Assign(kInputTypeNames[static_cast<size_t>(aInputType)]); +} + +/* static */ +EditorInputType InternalEditorInputEvent::GetEditorInputType( + const nsAString& aInputType) { + if (aInputType.IsEmpty()) { + return EditorInputType::eUnknown; + } + + if (!sInputTypeHashtable) { + sInputTypeHashtable = new InputTypeHashtable(ArrayLength(kInputTypeNames)); + for (size_t i = 0; i < ArrayLength(kInputTypeNames); i++) { + sInputTypeHashtable->InsertOrUpdate(nsDependentString(kInputTypeNames[i]), + static_cast<EditorInputType>(i)); + } + } + return sInputTypeHashtable->MaybeGet(aInputType) + .valueOr(EditorInputType::eUnknown); +} + +} // namespace mozilla |