summaryrefslogtreecommitdiffstats
path: root/widget/WidgetEventImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/WidgetEventImpl.cpp')
-rw-r--r--widget/WidgetEventImpl.cpp1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/widget/WidgetEventImpl.cpp b/widget/WidgetEventImpl.cpp
new file mode 100644
index 000000000..52e2b9b40
--- /dev/null
+++ b/widget/WidgetEventImpl.cpp
@@ -0,0 +1,1075 @@
+/* -*- 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 "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<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;
+ }
+}
+
+/******************************************************************************
+ * 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;
+}
+
+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 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 eBeforeKeyDown:
+ case eBeforeKeyUp:
+ case eAfterKeyDown:
+ case eAfterKeyUp:
+ 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::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:
+ 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;
+ }
+
+ // 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;
+ }
+}
+
+/******************************************************************************
+ * 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<double>(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;
+
+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)
+{
+ 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)
+{
+ 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<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 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<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");
+ 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<KeyNameIndex>(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<CodeNameIndex>(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<size_t>(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 nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
+ case CODE_NAME_INDEX_AltRight:
+ case CODE_NAME_INDEX_ControlRight:
+ case CODE_NAME_INDEX_OSRight:
+ case CODE_NAME_INDEX_ShiftRight:
+ return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
+ 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 nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+ default:
+ return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
+ }
+}
+
+/* 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