--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -392,17 +392,18 @@ TextEventDispatcher::DispatchKeyboardEve
}
bool
TextEventDispatcher::DispatchKeyboardEventInternal(
EventMessage aMessage,
const WidgetKeyboardEvent& aKeyboardEvent,
nsEventStatus& aStatus,
void* aData,
- uint32_t aIndexOfKeypress)
+ uint32_t aIndexOfKeypress,
+ bool aNeedsCallback)
{
// Note that this method is also used for dispatching key events on a plugin
// because key events on a plugin should be dispatched same as normal key
// events. Then, only some handlers which need to intercept key events
// before the focused plugin (e.g., reserved shortcut key handlers) can
// consume the events.
MOZ_ASSERT(WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage) ||
@@ -496,17 +497,17 @@ TextEventDispatcher::DispatchKeyboardEve
// Request the alternative char codes for the key event.
// eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
// needs to check if a following keypress event is reserved by chrome for
// stopping propagation of its preceding keydown event.
keyEvent.mAlternativeCharCodes.Clear();
if ((WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
aMessage == eKeyPress) &&
- (keyEvent.IsControl() || keyEvent.IsAlt() ||
+ (aNeedsCallback || keyEvent.IsControl() || keyEvent.IsAlt() ||
keyEvent.IsMeta() || keyEvent.IsOS())) {
nsCOMPtr<TextEventDispatcherListener> listener =
do_QueryReferent(mListener);
if (listener) {
DebugOnly<WidgetKeyboardEvent> original(keyEvent);
listener->WillDispatchKeyboardEvent(this, keyEvent, aIndexOfKeypress,
aData);
MOZ_ASSERT(keyEvent.mMessage ==
@@ -533,17 +534,18 @@ TextEventDispatcher::DispatchKeyboardEve
DispatchInputEvent(mWidget, keyEvent, aStatus);
return true;
}
bool
TextEventDispatcher::MaybeDispatchKeypressEvents(
const WidgetKeyboardEvent& aKeyboardEvent,
nsEventStatus& aStatus,
- void* aData)
+ void* aData,
+ bool aNeedsCallback)
{
// If the key event was consumed, keypress event shouldn't be fired.
if (aStatus == nsEventStatus_eConsumeNoDefault) {
return false;
}
// If the key shouldn't cause keypress events, don't fire them.
if (!aKeyboardEvent.ShouldCauseKeypressEvents()) {
@@ -558,17 +560,17 @@ TextEventDispatcher::MaybeDispatchKeypre
aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
1 : std::max(static_cast<nsAString::size_type>(1),
aKeyboardEvent.mKeyValue.Length());
bool isDispatched = false;
bool consumed = false;
for (size_t i = 0; i < keypressCount; i++) {
aStatus = nsEventStatus_eIgnore;
if (!DispatchKeyboardEventInternal(eKeyPress, aKeyboardEvent,
- aStatus, aData, i)) {
+ aStatus, aData, i, aNeedsCallback)) {
// The widget must have been gone.
break;
}
isDispatched = true;
if (!consumed) {
consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
}
}
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -3755,27 +3755,27 @@ function* runAccessKeyTests()
modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
"a", false);
yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
"A", false);
// Greek layout can activate a Latin accesskey
yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
- modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
"a", true);
yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
- modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
"A", true);
// ... and a Greek accesskey!
yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
- modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
"\u03b1", true);
yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
- modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
"\u0391", true);
// bug 359638
yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
modifiers:{shiftKey:1, altKey:1}, chars:".", unmodifiedChars:"."},
".", true);
}
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -1514,17 +1514,17 @@ NativeKey::InitWithKeyChar()
mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
if (IsKeyDownMessage()) {
// Compute some strings which may be inputted by the key with various
// modifier state if this key event won't cause text input actually.
// They will be used for setting mAlternativeCharCodes in the callback
// method which will be called by TextEventDispatcher.
- if (NeedsToHandleWithoutFollowingCharMessages()) {
+ if (!IsFollowedByPrintableCharMessage()) {
ComputeInputtingStringWithKeyboardLayout();
}
// Remove odd char messages if there are.
RemoveFollowingOddCharMessages();
}
}
void
@@ -1973,23 +1973,18 @@ NativeKey::InitKeyEvent(WidgetKeyboardEv
if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
}
aKeyEvent.mCodeNameIndex = mCodeNameIndex;
MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
aKeyEvent.mLocation = GetKeyLocation();
aModKeyState.InitInputEvent(aKeyEvent);
- NPEvent pluginEvent;
- if (aMsgSentToPlugin &&
- mWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
- pluginEvent.event = aMsgSentToPlugin->message;
- pluginEvent.wParam = aMsgSentToPlugin->wParam;
- pluginEvent.lParam = aMsgSentToPlugin->lParam;
- aKeyEvent.mPluginEvent.Copy(pluginEvent);
+ if (aMsgSentToPlugin) {
+ MaybeInitPluginEventOfKeyEvent(aKeyEvent, *aMsgSentToPlugin);
}
KeyboardLayout::NotifyIdleServiceOfUserActivity();
MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
"mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
"mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
@@ -2001,16 +1996,30 @@ NativeKey::InitKeyEvent(WidgetKeyboardEv
GetKeyLocationName(aKeyEvent.mLocation).get(),
GetModifiersName(aKeyEvent.mModifiers).get(),
GetBoolName(aKeyEvent.DefaultPrevented())));
return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault :
nsEventStatus_eIgnore;
}
+void
+NativeKey::MaybeInitPluginEventOfKeyEvent(WidgetKeyboardEvent& aKeyEvent,
+ const MSG& aMsgSentToPlugin) const
+{
+ if (mWidget->GetInputContext().mIMEState.mEnabled != IMEState::PLUGIN) {
+ return;
+ }
+ NPEvent pluginEvent;
+ pluginEvent.event = aMsgSentToPlugin.message;
+ pluginEvent.wParam = aMsgSentToPlugin.wParam;
+ pluginEvent.lParam = aMsgSentToPlugin.lParam;
+ aKeyEvent.mPluginEvent.Copy(pluginEvent);
+}
+
bool
NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const
{
nsCOMPtr<nsIAtom> command;
switch (aEventCommand) {
case APPCOMMAND_BROWSER_BACKWARD:
command = nsGkAtoms::Back;
break;
@@ -2439,54 +2448,35 @@ NativeKey::HandleKeyDownMessage(bool* aE
MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
"event because preceding keydown event was consumed",
this));
MaybeDispatchPluginEventsForRemovedCharMessages();
return true;
}
+ // If mCommittedCharsAndModifiers was initialized with following char
+ // messages, we should dispatch keypress events with its information.
+ if (IsFollowedByPrintableCharOrSysCharMessage()) {
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+ ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
+ "keypress events with retrieved char messages...", this));
+ return DispatchKeyPressEventsWithRetrievedCharMessages();
+ }
+
// If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
// keypress for almost all keys
if (NeedsToHandleWithoutFollowingCharMessages()) {
MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
"keypress events...", this));
return (MaybeDispatchPluginEventsForRemovedCharMessages() ||
DispatchKeyPressEventsWithoutCharMessage());
}
- if (!mFollowingCharMsgs.IsEmpty()) {
- bool consumed = false;
- for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::HandleKeyDownMessage(), stopped dispatching "
- "keypress events for remaining char messages, consumed=%s, "
- "mFollowingCharMsgs[%u]=%s, mMsg=%s, "
- "mFocusedWndBeforeDispatch=0x%p, ::GetFocus()=0x%p",
- this, GetBoolName(consumed), i,
- ToString(mFollowingCharMsgs[i]).get(),
- ToString(mMsg).get(), mFocusedWndBeforeDispatch, ::GetFocus()));
- consumed =
- DispatchKeyPressEventForFollowingCharMessage(mFollowingCharMsgs[i]) ||
- consumed;
- if (mWidget->Destroyed() || IsFocusedWindowChanged()) {
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::HandleKeyDownMessage(), %s event caused "
- "destroying the widget", this));
- return true;
- }
- }
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::HandleKeyDownMessage(), handled all following char "
- "messages, consumed=%s",
- this, GetBoolName(consumed)));
- return consumed;
- }
-
// If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
// dispatch keypress events.
if (mVirtualKeyCode == VK_PACKET) {
MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress event "
"because the key is VK_PACKET and there are no char messages",
this));
return false;
@@ -2710,26 +2700,20 @@ NativeKey::NeedsToHandleWithoutFollowing
// If following char message is for a control character, it should be handled
// without WM_CHAR message. This is typically Ctrl + [a-z].
if (mFollowingCharMsgs.Length() == 1 &&
IsControlCharMessage(mFollowingCharMsgs[0])) {
return true;
}
- // If inputting two or more characters, should be dispatched after removing
- // whole following char messages.
- if (mCommittedCharsAndModifiers.mLength > 1) {
- return true;
- }
-
- // If keydown message is followed by WM_CHAR whose wParam isn't a control
- // character, we should dispatch keypress event with the char message
- // even with any modifier state.
- if (IsFollowedByPrintableCharMessage()) {
+ // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
+ // a control character, we should dispatch keypress event with the char
+ // message even with any modifier state.
+ if (IsFollowedByPrintableCharOrSysCharMessage()) {
return false;
}
// If any modifier keys which may cause printable keys becoming non-printable
// are not pressed, we don't need special handling for the key.
if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
!mModKeyState.IsWin()) {
return false;
@@ -3199,16 +3183,66 @@ NativeKey::ComputeInputtingStringWithKey
mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
mInputtingStringAndModifiers.Clear();
mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
}
}
bool
+NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const
+{
+ MOZ_ASSERT(IsKeyDownMessage());
+ MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
+
+ nsresult rv = mDispatcher->BeginNativeInputTransaction();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
+ ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+ "FAILED due to BeginNativeInputTransaction() failure", this));
+ return true;
+ }
+ WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Debug,
+ ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+ "initializing keypress event...", this));
+ ModifierKeyState modKeyState(mModKeyState);
+ if (IsFollowedByPrintableCharMessage()) {
+ // If eKeyPress event should cause inputting text in focused editor,
+ // we need to remove Alt and Ctrl state.
+ modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
+ }
+ // We don't need to send char message here if there are two or more retrieved
+ // messages because we need to set each message to each eKeyPress event.
+ bool needsCallback = mFollowingCharMsgs.Length() > 1;
+ nsEventStatus status =
+ InitKeyEvent(keypressEvent, modKeyState,
+ !needsCallback ? &mFollowingCharMsgs[0] : nullptr);
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+ ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+ "dispatching keypress event(s)...", this));
+ bool dispatched =
+ mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
+ const_cast<NativeKey*>(this),
+ needsCallback);
+ if (mWidget->Destroyed()) {
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+ ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+ "keypress event(s) caused destroying the widget", this));
+ return true;
+ }
+ bool consumed = status == nsEventStatus_eConsumeNoDefault;
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+ ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+ "dispatched keypress event(s), dispatched=%s, consumed=%s",
+ this, GetBoolName(dispatched), GetBoolName(consumed)));
+ return consumed;
+}
+
+bool
NativeKey::DispatchKeyPressEventsWithoutCharMessage() const
{
MOZ_ASSERT(IsKeyDownMessage());
MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());
nsresult rv = mDispatcher->BeginNativeInputTransaction();
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
@@ -3245,16 +3279,56 @@ NativeKey::DispatchKeyPressEventsWithout
this, GetBoolName(dispatched), GetBoolName(consumed)));
return consumed;
}
void
NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
uint32_t aIndex)
{
+ // If it's an eKeyPress event and it's generated from retrieved char message,
+ // we need to set raw message information for plugins.
+ if (aKeyboardEvent.mMessage == eKeyPress &&
+ IsFollowedByPrintableCharOrSysCharMessage()) {
+ MOZ_RELEASE_ASSERT(aIndex < mCommittedCharsAndModifiers.mLength);
+ uint32_t foundPrintableCharMessages = 0;
+ for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
+ if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
+ // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
+ // WM_CHAR with a control character here? But we're not sure
+ // how can we create such message queue (i.e., WM_CHAR or
+ // WM_SYSCHAR with a printable character and such message are
+ // generated by a keydown). So, let's ignore such case until
+ // we'd get some bug reports.
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
+ ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
+ "ignoring %uth message due to non-printable char message, %s",
+ this, i + 1, ToString(mFollowingCharMsgs[i]).get()));
+ continue;
+ }
+ if (foundPrintableCharMessages++ == aIndex) {
+ // Found message which caused the eKeyPress event. Let's set the
+ // message for plugin if it's necessary.
+ MaybeInitPluginEventOfKeyEvent(aKeyboardEvent, mFollowingCharMsgs[i]);
+ break;
+ }
+ }
+ // Set modifier state from mCommittedCharsAndModifiers because some of them
+ // might be different. For example, Shift key was pressed at inputting
+ // dead char but Shift key was released before inputting next character.
+ ModifierKeyState modKeyState(mModKeyState);
+ modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
+ MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
+ modKeyState.Set(mCommittedCharsAndModifiers.mModifiers[aIndex]);
+ modKeyState.InitInputEvent(aKeyboardEvent);
+ MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+ ("%p NativeKey::WillDispatchKeyboardEvent(), "
+ "setting %uth modifier state to %s",
+ this, aIndex + 1, ToString(modKeyState).get()));
+ }
uint32_t longestLength =
std::max(mInputtingStringAndModifiers.mLength,
std::max(mShiftedString.mLength, mUnshiftedString.mLength));
uint32_t skipUniChars = longestLength - mInputtingStringAndModifiers.mLength;
uint32_t skipShiftedChars = longestLength - mShiftedString.mLength;
uint32_t skipUnshiftedChars = longestLength - mUnshiftedString.mLength;
if (aIndex >= longestLength) {
MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
@@ -3379,87 +3453,16 @@ NativeKey::WillDispatchKeyboardEvent(Wid
charForOEMKeyCode != mUnshiftedLatinChar &&
charForOEMKeyCode != mShiftedLatinChar) {
AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
altArray.AppendElement(OEMChars);
}
}
}
-bool
-NativeKey::DispatchKeyPressEventForFollowingCharMessage(
- const MSG& aCharMsg) const
-{
- MOZ_ASSERT(IsKeyDownMessage());
-
- if (mFakeCharMsgs) {
- if (IsDeadCharMessage(aCharMsg)) {
- return false;
- }
-#ifdef DEBUG
- if (mIsPrintableKey) {
- nsPrintfCString log(
- "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ "
- "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, "
- "wParam=0x%04X",
- mOriginalVirtualKeyCode, mCommittedCharsAndModifiers.mChars[0],
- mCommittedCharsAndModifiers.mChars[1],
- mCommittedCharsAndModifiers.mChars[2],
- mCommittedCharsAndModifiers.mChars[3],
- mCommittedCharsAndModifiers.mChars[4],
- mCommittedCharsAndModifiers.mLength, aCharMsg.wParam);
- if (mCommittedCharsAndModifiers.IsEmpty()) {
- log.Insert("length is zero: ", 0);
- NS_ERROR(log.get());
- NS_ABORT();
- } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) {
- log.Insert("character mismatch: ", 0);
- NS_ERROR(log.get());
- NS_ABORT();
- }
- }
-#endif // #ifdef DEBUG
- return HandleCharMessage(aCharMsg);
- }
-
- if (IsDeadCharMessage(aCharMsg)) {
- if (!mWidget->PluginHasFocus()) {
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
- "plugin doesn't have focus", this));
- return false;
- }
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
- "dispatching plugin event...", this));
- bool ok = mWidget->DispatchPluginEvent(aCharMsg) || mWidget->Destroyed();
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
- "dispatched plugin event, result=%s, mWidget->Destroyed()=%s",
- this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
- }
-
- bool defaultPrevented = HandleCharMessage(aCharMsg);
- // If a syschar keypress wasn't processed, Windows may want to
- // handle it to activate a native menu.
- if (!defaultPrevented && IsSysCharMessage(aCharMsg)) {
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
- "calling DefWindowProcW(aCharMsg=%s)...",
- this, ToString(aCharMsg).get()));
- ::DefWindowProcW(aCharMsg.hwnd, aCharMsg.message,
- aCharMsg.wParam, aCharMsg.lParam);
- MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
- ("%p NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
- "called DefWindowProcW(aCharMsg=%s)",
- this, ToString(aCharMsg).get()));
- }
- return defaultPrevented;
-}
-
/*****************************************************************************
* mozilla::widget::KeyboardLayout
*****************************************************************************/
KeyboardLayout* KeyboardLayout::sInstance = nullptr;
nsIIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;
// This log is very noisy if you don't want to retrieve the mapping table