diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index c8f24a8e064e..581c0cfe5fd4 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -104,6 +104,18 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, { #ifdef NS_ENABLE_TSF if (IsTSFAvailable()) { + if (aMessage == WM_IME_SETCONTEXT) { + // If a windowless plugin had focus and IME was handled on it, composition + // window was set the position. After that, even in TSF mode, WinXP keeps + // to use composition window at the position if the active IME is not + // aware TSF. For avoiding this issue, we need to hide the composition + // window here. + if (aWParam) { + aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; + } + return false; + } + if (aMessage == WM_USER_TSF_TEXTCHANGE) { nsTextStore::OnTextChangeMsg(); aResult.mConsumed = true; diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index b331b4ef80f1..fdc50cad6ab8 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -763,6 +763,8 @@ nsIMM32Handler::OnIMEStartCompositionOnPlugin(nsWindow* aWindow, aWindow->GetWindowHandle(), mIsComposingOnPlugin ? "TRUE" : "FALSE")); mIsComposingOnPlugin = true; mComposingWindow = aWindow; + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext); aResult.mConsumed = aWindow->DispatchPluginEvent(WM_IME_STARTCOMPOSITION, wParam, lParam, false); @@ -795,6 +797,8 @@ nsIMM32Handler::OnIMECompositionOnPlugin(nsWindow* aWindow, if (IS_COMPOSING_LPARAM(lParam)) { mIsComposingOnPlugin = true; mComposingWindow = aWindow; + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext); } aResult.mConsumed = aWindow->DispatchPluginEvent(WM_IME_COMPOSITION, wParam, lParam, true); @@ -813,6 +817,12 @@ nsIMM32Handler::OnIMEEndCompositionOnPlugin(nsWindow* aWindow, mIsComposingOnPlugin = false; mComposingWindow = nullptr; + + if (mNativeCaretIsCreated) { + ::DestroyCaret(); + mNativeCaretIsCreated = false; + } + aResult.mConsumed = aWindow->DispatchPluginEvent(WM_IME_ENDCOMPOSITION, wParam, lParam, false); @@ -1930,6 +1940,68 @@ nsIMM32Handler::SetIMERelatedWindowsPos(nsWindow* aWindow, return true; } +void +nsIMM32Handler::SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow, + const nsIMEContext& aIMEContext) +{ + WidgetQueryContentEvent editorRectEvent(true, NS_QUERY_EDITOR_RECT, aWindow); + aWindow->InitEvent(editorRectEvent); + aWindow->DispatchWindowEvent(&editorRectEvent); + if (!editorRectEvent.mSucceeded) { + PR_LOG(gIMM32Log, PR_LOG_ALWAYS, + ("IMM32: SetIMERelatedWindowsPosOnPlugin, " + "FAILED (NS_QUERY_EDITOR_RECT)")); + return; + } + + // Clip the plugin rect by the client rect of the window because composition + // window needs to be specified the position in the client area. + nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false); + nsIntRect pluginRectInScreen = + editorRectEvent.mReply.mRect + toplevelWindow->WidgetToScreenOffset(); + nsIntRect winRectInScreen; + aWindow->GetClientBounds(winRectInScreen); + // composition window cannot be positioned on the edge of client area. + winRectInScreen.width--; + winRectInScreen.height--; + nsIntRect clippedPluginRect; + clippedPluginRect.x = + std::min(std::max(pluginRectInScreen.x, winRectInScreen.x), + winRectInScreen.XMost()); + clippedPluginRect.y = + std::min(std::max(pluginRectInScreen.y, winRectInScreen.y), + winRectInScreen.YMost()); + int32_t xMost = std::min(pluginRectInScreen.XMost(), winRectInScreen.XMost()); + int32_t yMost = std::min(pluginRectInScreen.YMost(), winRectInScreen.YMost()); + clippedPluginRect.width = std::max(0, xMost - clippedPluginRect.x); + clippedPluginRect.height = std::max(0, yMost - clippedPluginRect.y); + clippedPluginRect -= aWindow->WidgetToScreenOffset(); + + // Cover the plugin with native caret. This prevents IME's window and plugin + // overlap. + if (mNativeCaretIsCreated) { + ::DestroyCaret(); + } + mNativeCaretIsCreated = + ::CreateCaret(aWindow->GetWindowHandle(), nullptr, + clippedPluginRect.width, clippedPluginRect.height); + ::SetCaretPos(clippedPluginRect.x, clippedPluginRect.y); + + // Set the composition window to bottom-left of the clipped plugin. + // As far as we know, there is no IME for RTL language. Therefore, this code + // must not need to take care of RTL environment. + COMPOSITIONFORM compForm; + compForm.dwStyle = CFS_POINT; + compForm.ptCurrentPos.x = clippedPluginRect.BottomLeft().x; + compForm.ptCurrentPos.y = clippedPluginRect.BottomLeft().y; + if (!::ImmSetCompositionWindow(aIMEContext.get(), &compForm)) { + PR_LOG(gIMM32Log, PR_LOG_ALWAYS, + ("IMM32: SetIMERelatedWindowsPosOnPlugin, " + "FAILED to set composition window")); + return; + } +} + void nsIMM32Handler::ResolveIMECaretPos(nsIWidget* aReferenceWidget, nsIntRect& aCursorRect, diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index f196d2feff59..48199d3b4486 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -261,7 +261,9 @@ protected: nsACString& aANSIStr); bool SetIMERelatedWindowsPos(nsWindow* aWindow, - const nsIMEContext &aIMEContext); + const nsIMEContext& aIMEContext); + void SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow, + const nsIMEContext& aIMEContext); bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow, uint32_t aOffset, nsIntRect &aCharRect);