Bug 610821 part.1 Redirect native keydown message to IME if focus is moved to an editor by keydown event handler r=jmathies, a=beltzner

This commit is contained in:
Masayuki Nakano 2011-01-14 22:38:57 +09:00
Родитель 5d7ab8fe1b
Коммит 2eb660ea5f
2 изменённых файлов: 176 добавлений и 9 удалений

Просмотреть файл

@ -316,6 +316,8 @@ PRUint32 nsWindow::sOOPPPluginFocusEvent =
RegisterWindowMessageW(kOOPPPluginFocusEventId);
#endif
MSG nsWindow::sRedirectedKeyDown;
/**************************************************************
*
* SECTION: globals variables
@ -460,6 +462,8 @@ nsWindow::nsWindow() : nsBaseWidget()
nsUXThemeData::InitTitlebarInfo();
// Init theme data
nsUXThemeData::UpdateNativeThemeInfo();
ForgetRedirectedKeyDownMessage();
} // !sInstanceCount
mIdleService = nsnull;
@ -5322,6 +5326,11 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
#endif
case WM_SETFOCUS:
// If previous focused window isn't ours, it must have received the
// redirected message. So, we should forget it.
if (!IsOurProcessWindow(HWND(wParam))) {
ForgetRedirectedKeyDownMessage();
}
if (sJustGotActivate) {
result = DispatchFocusToTopLevelWindow(NS_ACTIVATE);
}
@ -5880,6 +5889,25 @@ void nsWindow::PostSleepWakeNotification(const char* aNotification)
}
#endif
// RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
// message handler. If there is no WM_(SYS)CHAR message for it, this
// method does nothing.
// NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
// called in message loop. So, WM_(SYS)KEYDOWN message should have
// WM_(SYS)CHAR message in the queue if the keydown event causes character
// input.
/* static */
void nsWindow::RemoveNextCharMessage(HWND aWnd)
{
MSG msg;
if (::PeekMessageW(&msg, aWnd,
WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD) &&
(msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
::GetMessageW(&msg, aWnd, msg.message, msg.message);
}
}
LRESULT nsWindow::ProcessCharMessage(const MSG &aMsg, PRBool *aEventDispatched)
{
NS_PRECONDITION(aMsg.message == WM_CHAR || aMsg.message == WM_SYSCHAR,
@ -5941,6 +5969,12 @@ LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg,
NS_PRECONDITION(aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN,
"message is not keydown event");
// If this method doesn't call OnKeyDown(), this method must clean up the
// redirected message information itself. For more information, see above
// comment of AutoForgetRedirectedKeyDownMessage struct definition in
// nsWindow.h.
AutoForgetRedirectedKeyDownMessage forgetRedirectedMessage(this, aMsg);
nsModifierKeyState modKeyState;
// Note: the original code passed (HIWORD(lParam)) to OnKeyDown as
@ -5962,6 +5996,9 @@ LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg,
nsIMM32Handler::NotifyEndStatusChange();
} else if (!nsIMM32Handler::IsComposingOn(this)) {
result = OnKeyDown(aMsg, modKeyState, aEventDispatched, nsnull);
// OnKeyDown cleaned up the redirected message information itself, so,
// we should do nothing.
forgetRedirectedMessage.mCancel = PR_TRUE;
}
#ifndef WINCE
@ -6673,6 +6710,14 @@ UINT nsWindow::MapFromNativeToDOM(UINT aNativeKeyCode)
return aNativeKeyCode;
}
/* static */
PRBool nsWindow::IsRedirectedKeyDownMessage(const MSG &aMsg)
{
return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
(sRedirectedKeyDown.message == aMsg.message &&
GetScanCode(sRedirectedKeyDown.lParam) == GetScanCode(aMsg.lParam));
}
/**
* nsWindow::OnKeyDown peeks into the message queue and pulls out
* WM_CHAR messages for processing. During testing we don't want to
@ -6686,14 +6731,15 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
PRBool *aEventDispatched,
nsFakeCharMessage* aFakeCharMessage)
{
UINT virtualKeyCode = aMsg.wParam;
UINT virtualKeyCode =
aMsg.wParam != VK_PROCESSKEY ? aMsg.wParam : ::ImmGetVirtualKey(mWnd);
#ifndef WINCE
gKbdLayout.OnKeyDown (virtualKeyCode);
gKbdLayout.OnKeyDown(virtualKeyCode);
#endif
// Use only DOMKeyCode for XP processing.
// Use aVirtualKeyCode for gKbdLayout and native processing.
// Use virtualKeyCode for gKbdLayout and native processing.
UINT DOMKeyCode = nsIMM32Handler::IsComposingOn(this) ?
virtualKeyCode : MapFromNativeToDOM(virtualKeyCode);
@ -6701,10 +6747,71 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
//printf("In OnKeyDown virt: %d\n", DOMKeyCode);
#endif
PRBool noDefault =
DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, &aMsg, aModKeyState);
if (aEventDispatched)
*aEventDispatched = PR_TRUE;
static PRBool sRedirectedKeyDownEventPreventedDefault = PR_FALSE;
PRBool noDefault;
if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
HIMC oldIMC = mOldIMC;
noDefault =
DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, &aMsg, aModKeyState);
if (aEventDispatched) {
*aEventDispatched = PR_TRUE;
}
// If IMC wasn't associated to the window but is associated it now (i.e.,
// focus is moved from a non-editable editor to an editor by keydown
// event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
// inputting if IME is opened. But then, we should redirect the native
// keydown message to IME.
// However, note that if focus has been already moved to another
// application, we shouldn't redirect the message to it because the keydown
// message is processed by us, so, nobody shouldn't process it.
HWND focusedWnd = ::GetFocus();
if (!noDefault && !aFakeCharMessage && oldIMC && !mOldIMC && focusedWnd &&
!PluginHasFocus()) {
RemoveNextCharMessage(focusedWnd);
INPUT keyinput;
keyinput.type = INPUT_KEYBOARD;
keyinput.ki.wVk = aMsg.wParam;
keyinput.ki.wScan = GetScanCode(aMsg.lParam);
keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
if (IsExtendedScanCode(aMsg.lParam)) {
keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
keyinput.ki.time = 0;
keyinput.ki.dwExtraInfo = NULL;
sRedirectedKeyDownEventPreventedDefault = noDefault;
sRedirectedKeyDown = aMsg;
::SendInput(1, &keyinput, sizeof(keyinput));
// Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
// If it's needed, it will be dispatched after next (redirected)
// WM_KEYDOWN.
return PR_TRUE;
}
if (mOnDestroyCalled) {
// If this was destroyed by the keydown event handler, we shouldn't
// dispatch keypress event on this window.
return PR_TRUE;
}
} else {
noDefault = sRedirectedKeyDownEventPreventedDefault;
// If this is redirected keydown message, we have dispatched the keydown
// event already.
if (aEventDispatched) {
*aEventDispatched = PR_TRUE;
}
}
ForgetRedirectedKeyDownMessage();
// If the key was processed by IME, we shouldn't dispatch keypress event.
if (aMsg.wParam == VK_PROCESSKEY) {
return noDefault;
}
// If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a keypress
// for almost all keys
@ -7408,14 +7515,19 @@ void nsWindow::OnSettingsChange(WPARAM wParam, LPARAM lParam)
nsWindowGfx::OnSettingsChangeGfx(wParam);
}
static PRBool IsOurProcessWindow(HWND aHWND)
/* static */
PRBool nsWindow::IsOurProcessWindow(HWND aHWND)
{
if (!aHWND) {
return PR_FALSE;
}
DWORD processId = 0;
::GetWindowThreadProcessId(aHWND, &processId);
return processId == ::GetCurrentProcessId();
}
static HWND FindOurProcessWindow(HWND aHWND)
/* static */
HWND nsWindow::FindOurProcessWindow(HWND aHWND)
{
for (HWND wnd = ::GetParent(aHWND); wnd; wnd = ::GetParent(wnd)) {
if (IsOurProcessWindow(wnd)) {

Просмотреть файл

@ -336,6 +336,8 @@ protected:
return mTransparencyMode == eTransparencyGlass ||
mTransparencyMode == eTransparencyBorderlessGlass;
}
PRBool IsOurProcessWindow(HWND aHWND);
HWND FindOurProcessWindow(HWND aHWND);
/**
* Event processing helpers
@ -346,6 +348,7 @@ protected:
PRBool DispatchStandardEvent(PRUint32 aMsg);
PRBool DispatchCommandEvent(PRUint32 aEventCommand);
void RelayMouseEvent(UINT aMsg, WPARAM wParam, LPARAM lParam);
static void RemoveNextCharMessage(HWND aWnd);
void RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg, UINT aLastMsg);
static MSG InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam);
virtual PRBool ProcessMessage(UINT msg, WPARAM &wParam,
@ -368,6 +371,19 @@ protected:
LRESULT* aRetValue,
PRBool& aQuitProcessing);
PRInt32 ClientMarginHitTestPoint(PRInt32 mx, PRInt32 my);
static WORD GetScanCode(LPARAM aLParam)
{
return (aLParam >> 16) & 0xFF;
}
static PRBool IsExtendedScanCode(LPARAM aLParam)
{
return (aLParam & 0x1000000) != 0;
}
static PRBool IsRedirectedKeyDownMessage(const MSG &aMsg);
static void ForgetRedirectedKeyDownMessage()
{
sRedirectedKeyDown.message = WM_NULL;
}
/**
* Event handlers
@ -608,6 +624,45 @@ protected:
static HINSTANCE sAccLib;
static LPFNLRESULTFROMOBJECT sLresultFromObject;
#endif // ACCESSIBILITY
// sRedirectedKeyDown is WM_KEYDOWN message or WM_SYSKEYDOWN message which
// was reirected to SendInput() API by OnKeyDown().
static MSG sRedirectedKeyDown;
// If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
// redirected message, OnKeyDowm() prevents to dispatch NS_KEY_DOWN event
// because it has been dispatched before the message was redirected.
// However, in some cases, ProcessKeyDownMessage() doesn't call OnKeyDown().
// Then, ProcessKeyDownMessage() needs to forget the redirected message and
// remove WM_CHAR message or WM_SYSCHAR message for the redirected keydown
// message. AutoForgetRedirectedKeyDownMessage struct is a helper struct
// for doing that. This must be created in stack.
struct AutoForgetRedirectedKeyDownMessage
{
AutoForgetRedirectedKeyDownMessage(nsWindow* aWindow, const MSG &aMsg) :
mCancel(!nsWindow::IsRedirectedKeyDownMessage(aMsg)),
mWindow(aWindow), mMsg(aMsg)
{
}
~AutoForgetRedirectedKeyDownMessage()
{
if (mCancel) {
return;
}
// Prevent unnecessary keypress event
if (!mWindow->mOnDestroyCalled) {
nsWindow::RemoveNextCharMessage(mWindow->mWnd);
}
// Foreget the redirected message
nsWindow::ForgetRedirectedKeyDownMessage();
}
PRBool mCancel;
nsCOMPtr<nsWindow> mWindow;
const MSG &mMsg;
};
};
/**