Bug 1208944 - Part 9. Hook IMM32 APIs on plugin process. r=masayuki

This commit is contained in:
Makoto Kato 2015-12-29 22:57:38 +09:00
parent 52c8bbab4e
commit 8683353ed6
23 changed files with 562 additions and 3 deletions

View File

@ -281,6 +281,11 @@ parent:
*/
prio(urgent) async SetPluginFocused(bool aFocused);
/**
* Set IME candidate window by windowless plugin if plugin has focus.
*/
async SetCandidateWindowForPlugin(int32_t aX, int32_t aY);
/**
* Request that the parent process move focus to the browser's frame. If
* canRaise is true, the window can be raised if it is inactive.

View File

@ -2396,6 +2396,19 @@ TabParent::RecvSetPluginFocused(const bool& aFocused)
return true;
}
bool
TabParent::RecvSetCandidateWindowForPlugin(const int32_t& aX,
const int32_t& aY)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return true;
}
widget->SetCandidateWindowForPlugin(aX, aY);
return true;
}
bool
TabParent::RecvGetInputContext(int32_t* aIMEEnabled,
int32_t* aIMEOpen)

View File

@ -196,6 +196,8 @@ public:
const int32_t& aPanelY,
nsString* aCommitted) override;
virtual bool RecvSetPluginFocused(const bool& aFocused) override;
virtual bool RecvSetCandidateWindowForPlugin(const int32_t& aX,
const int32_t& aY) override;
virtual bool RecvGetInputContext(int32_t* aIMEEnabled,
int32_t* aIMEOpen) override;
virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,

View File

@ -60,6 +60,8 @@ using mozilla::DefaultXDisplay;
#include "nsFrameSelection.h"
#include "PuppetWidget.h"
#include "nsPIWindowRoot.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/TextComposition.h"
#include "nsContentCID.h"
#include "nsWidgetsCID.h"
@ -791,7 +793,182 @@ nsPluginInstanceOwner::SetNetscapeWindowAsParent(HWND aWindowToAdopt)
reinterpret_cast<uintptr_t>(aWindowToAdopt));
return NS_OK;
}
#endif
bool
nsPluginInstanceOwner::GetCompositionString(uint32_t aType,
nsTArray<uint8_t>* aDist,
int32_t* aLength)
{
RefPtr<TextComposition> composition = GetTextComposition();
if (NS_WARN_IF(!composition)) {
return false;
}
switch(aType) {
case GCS_COMPSTR: {
if (!composition->IsComposing()) {
*aLength = 0;
return true;
}
uint32_t len = composition->LastData().Length() * sizeof(char16_t);
if (len) {
aDist->SetLength(len);
memcpy(aDist->Elements(), composition->LastData().get(), len);
}
*aLength = len;
return true;
}
case GCS_RESULTSTR: {
if (composition->IsComposing()) {
*aLength = 0;
return true;
}
uint32_t len = composition->LastData().Length() * sizeof(char16_t);
if (len) {
aDist->SetLength(len);
memcpy(aDist->Elements(), composition->LastData().get(), len);
}
*aLength = len;
return true;
}
case GCS_CURSORPOS: {
*aLength = 0;
TextRangeArray* ranges = composition->GetLastRanges();
if (!ranges) {
return true;
}
*aLength = ranges->GetCaretPosition();
if (*aLength < 0) {
return false;
}
return true;
}
case GCS_COMPATTR: {
TextRangeArray* ranges = composition->GetLastRanges();
if (!ranges || ranges->IsEmpty()) {
*aLength = 0;
return true;
}
aDist->SetLength(composition->LastData().Length());
memset(aDist->Elements(), ATTR_INPUT, aDist->Length());
for (TextRange& range : *ranges) {
uint8_t type = ATTR_INPUT;
switch(range.mRangeType) {
case NS_TEXTRANGE_RAWINPUT:
type = ATTR_INPUT;
break;
case NS_TEXTRANGE_SELECTEDRAWTEXT:
type = ATTR_TARGET_NOTCONVERTED;
break;
case NS_TEXTRANGE_CONVERTEDTEXT:
type = ATTR_CONVERTED;
break;
case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT:
type = ATTR_TARGET_CONVERTED;
break;
default:
continue;
}
size_t minLen = std::min<size_t>(range.mEndOffset, aDist->Length());
for (size_t i = range.mStartOffset; i < minLen; i++) {
(*aDist)[i] = type;
}
}
*aLength = aDist->Length();
return true;
}
case GCS_COMPCLAUSE: {
RefPtr<TextRangeArray> ranges = composition->GetLastRanges();
if (!ranges || ranges->IsEmpty()) {
aDist->SetLength(sizeof(uint32_t));
memset(aDist->Elements(), 0, sizeof(uint32_t));
*aLength = aDist->Length();
return true;
}
nsAutoTArray<uint32_t, 16> clauses;
clauses.AppendElement(0);
for (TextRange& range : *ranges) {
if (!range.IsClause()) {
continue;
}
clauses.AppendElement(range.mEndOffset);
}
aDist->SetLength(clauses.Length() * sizeof(uint32_t));
memcpy(aDist->Elements(), clauses.Elements(), aDist->Length());
*aLength = aDist->Length();
return true;
}
case GCS_RESULTREADSTR: {
// When returning error causes unexpected error, so we return 0 instead.
*aLength = 0;
return true;
}
case GCS_RESULTCLAUSE: {
// When returning error causes unexpected error, so we return 0 instead.
*aLength = 0;
return true;
}
default:
NS_WARNING(
nsPrintfCString("Unsupported type %x of ImmGetCompositionStringW hook",
aType).get());
break;
}
return false;
}
bool
nsPluginInstanceOwner::SetCandidateWindow(int32_t aX, int32_t aY)
{
if (NS_WARN_IF(!mPluginFrame)) {
return false;
}
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
if (!widget) {
widget = GetRootWidgetForPluginFrame(mPluginFrame);
if (NS_WARN_IF(!widget)) {
return false;
}
}
widget->SetCandidateWindowForPlugin(aX, aY);
return true;
}
bool
nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted)
{
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
if (!widget) {
widget = GetRootWidgetForPluginFrame(mPluginFrame);
if (NS_WARN_IF(!widget)) {
return false;
}
}
if (aCommitted) {
widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION);
} else {
widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION);
}
return true;
}
#endif // #ifdef XP_WIN
NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
{
@ -1597,6 +1774,32 @@ nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
return NS_OK;
}
#ifdef XP_WIN
already_AddRefed<TextComposition>
nsPluginInstanceOwner::GetTextComposition()
{
if (NS_WARN_IF(!mPluginFrame)) {
return nullptr;
}
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
if (!widget) {
widget = GetRootWidgetForPluginFrame(mPluginFrame);
if (NS_WARN_IF(!widget)) {
return nullptr;
}
}
RefPtr<TextComposition> composition =
IMEStateManager::GetTextCompositionFor(widget);
if (NS_WARN_IF(!composition)) {
return nullptr;
}
return composition.forget();
}
#endif
nsresult
nsPluginInstanceOwner::DispatchCompositionToPlugin(nsIDOMEvent* aEvent)
{
@ -1611,6 +1814,16 @@ nsPluginInstanceOwner::DispatchCompositionToPlugin(nsIDOMEvent* aEvent)
if (NS_WARN_IF(!compositionEvent)) {
return NS_ERROR_INVALID_ARG;
}
if (compositionEvent->mMessage == eCompositionChange) {
RefPtr<TextComposition> composition = GetTextComposition();
if (NS_WARN_IF(!composition)) {
return NS_ERROR_FAILURE;
}
TextComposition::CompositionChangeEventHandlingMarker
compositionChangeEventHandlingMarker(composition, compositionEvent);
}
nsEventStatus rv = ProcessEvent(*compositionEvent);
// XXX This isn't e10s aware.
// If the event isn't consumed, we cannot post result to chrome process.

View File

@ -29,6 +29,7 @@ class nsPluginFrame;
class nsDisplayListBuilder;
namespace mozilla {
class TextComposition;
namespace dom {
struct MozPluginParameter;
} // namespace dom
@ -260,6 +261,11 @@ public:
void NotifyHostCreateWidget();
void NotifyDestroyPending();
bool GetCompositionString(uint32_t aIndex, nsTArray<uint8_t>* aString,
int32_t* aLength);
bool SetCandidateWindow(int32_t aX, int32_t aY);
bool RequestCommitOrCancel(bool aCommitted);
private:
virtual ~nsPluginInstanceOwner();
@ -282,6 +288,7 @@ private:
#if defined(XP_WIN)
nsIWidget* GetContainingWidgetIfOffset();
already_AddRefed<mozilla::TextComposition> GetTextComposition();
#endif
nsPluginNativeWindow *mPluginWindow;

View File

@ -260,6 +260,15 @@ parent:
// returned by NPN_GetValue_NPNVnetscapeWindow. Only used on Windows.
async SetNetscapeWindowAsParent(NativeWindowHandle childWindow);
sync GetCompositionString(uint32_t aType)
returns (uint8_t[] aDist, int32_t aLength);
// Set candidate window position.
//
// @param aX x position of candidate window
// @param aY y position of candidate window
async SetCandidateWindow(int32_t aX, int32_t aY);
sync RequestCommitOrCancel(bool aCommitted);
both:
async PPluginScriptableObject();

View File

@ -73,6 +73,25 @@ static WindowsDllInterceptor sUser32Intercept;
static HWND sWinlessPopupSurrogateHWND = nullptr;
static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr;
typedef HIMC (WINAPI *Imm32ImmGetContext)(HWND hWND);
typedef BOOL (WINAPI *Imm32ImmReleaseContext)(HWND hWND, HIMC hIMC);
typedef LONG (WINAPI *Imm32ImmGetCompositionString)(HIMC hIMC,
DWORD dwIndex,
LPVOID lpBuf,
DWORD dwBufLen);
typedef BOOL (WINAPI *Imm32ImmSetCandidateWindow)(HIMC hIMC,
LPCANDIDATEFORM lpCandidate);
typedef BOOL (WINAPI *Imm32ImmNotifyIME)(HIMC hIMC, DWORD dwAction,
DWORD dwIndex, DWORD dwValue);
static WindowsDllInterceptor sImm32Intercept;
static Imm32ImmGetContext sImm32ImmGetContextStub = nullptr;
static Imm32ImmReleaseContext sImm32ImmReleaseContextStub = nullptr;
static Imm32ImmGetCompositionString sImm32ImmGetCompositionStringStub = nullptr;
static Imm32ImmSetCandidateWindow sImm32ImmSetCandidateWindowStub = nullptr;
static Imm32ImmNotifyIME sImm32ImmNotifyIME = nullptr;
static PluginInstanceChild* sCurrentPluginInstance = nullptr;
static const HIMC sHookIMC = (const HIMC)0xefefefef;
using mozilla::gfx::SharedDIB;
#include <windows.h>
@ -197,6 +216,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
SetUnityHooks();
}
InitImm32Hook();
#endif // OS_WIN
}
@ -1983,6 +2003,135 @@ PluginInstanceChild::CreateWinlessPopupSurrogate()
SendSetNetscapeWindowAsParent(mWinlessPopupSurrogateHWND);
}
// static
HIMC
PluginInstanceChild::ImmGetContextProc(HWND aWND)
{
if (!sCurrentPluginInstance) {
return sImm32ImmGetContextStub(aWND);
}
wchar_t szClass[21];
int haveClass = GetClassNameW(aWND, szClass, ArrayLength(szClass));
if (!haveClass || wcscmp(szClass, L"SWFlash_PlaceholderX")) {
NS_WARNING("We cannot recongnize hooked window class");
return sImm32ImmGetContextStub(aWND);
}
return sHookIMC;
}
// static
BOOL
PluginInstanceChild::ImmReleaseContextProc(HWND aWND, HIMC aIMC)
{
if (aIMC == sHookIMC) {
return TRUE;
}
return sImm32ImmReleaseContextStub(aWND, aIMC);
}
// static
LONG
PluginInstanceChild::ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
LPVOID aBuf, DWORD aLen)
{
if (aIMC != sHookIMC) {
return sImm32ImmGetCompositionStringStub(aIMC, aIndex, aBuf, aLen);
}
if (!sCurrentPluginInstance) {
return IMM_ERROR_GENERAL;
}
nsAutoTArray<uint8_t, 16> dist;
int32_t length = 0; // IMM_ERROR_NODATA
sCurrentPluginInstance->SendGetCompositionString(aIndex, &dist, &length);
if (length == IMM_ERROR_NODATA || length == IMM_ERROR_GENERAL) {
return length;
}
if (aBuf && aLen >= static_cast<DWORD>(length)) {
memcpy(aBuf, dist.Elements(), length);
}
return length;
}
// staitc
BOOL
PluginInstanceChild::ImmSetCandidateWindowProc(HIMC aIMC, LPCANDIDATEFORM aForm)
{
if (aIMC != sHookIMC) {
return sImm32ImmSetCandidateWindowStub(aIMC, aForm);
}
if (!sCurrentPluginInstance ||
aForm->dwIndex != 0 ||
!(aForm->dwStyle & CFS_CANDIDATEPOS)) {
// Flash only uses CFS_CANDIDATEPOS with index == 0.
return FALSE;
}
sCurrentPluginInstance->SendSetCandidateWindow(
aForm->ptCurrentPos.x, aForm->ptCurrentPos.y);
return TRUE;
}
// static
BOOL
PluginInstanceChild::ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
DWORD aValue)
{
if (aIMC != sHookIMC) {
return sImm32ImmNotifyIME(aIMC, aAction, aIndex, aValue);
}
// We only supports NI_COMPOSITIONSTR because Flash uses it only
if (!sCurrentPluginInstance ||
aAction != NI_COMPOSITIONSTR ||
(aIndex != CPS_COMPLETE && aIndex != CPS_CANCEL)) {
return FALSE;
}
sCurrentPluginInstance->SendRequestCommitOrCancel(aAction == CPS_COMPLETE);
return TRUE;
}
void
PluginInstanceChild::InitImm32Hook()
{
if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
return;
}
if (sImm32ImmGetContextStub) {
return;
}
// When using windowless plugin, IMM API won't work due ot OOP.
sImm32Intercept.Init("imm32.dll");
sImm32Intercept.AddHook(
"ImmGetContext",
reinterpret_cast<intptr_t>(ImmGetContextProc),
(void**)&sImm32ImmGetContextStub);
sImm32Intercept.AddHook(
"ImmReleaseContext",
reinterpret_cast<intptr_t>(ImmReleaseContextProc),
(void**)&sImm32ImmReleaseContextStub);
sImm32Intercept.AddHook(
"ImmGetCompositionStringW",
reinterpret_cast<intptr_t>(ImmGetCompositionStringProc),
(void**)&sImm32ImmGetCompositionStringStub);
sImm32Intercept.AddHook(
"ImmSetCandidateWindow",
reinterpret_cast<intptr_t>(ImmSetCandidateWindowProc),
(void**)&sImm32ImmSetCandidateWindowStub);
sImm32Intercept.AddHook(
"ImmNotifyIME",
reinterpret_cast<intptr_t>(ImmNotifyIME),
(void**)&sImm32ImmNotifyIME);
}
void
PluginInstanceChild::DestroyWinlessPopupSurrogate()
{
@ -2016,6 +2165,13 @@ PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
focusHwnd = SetFocus(mWinlessPopupSurrogateHWND);
}
AutoRestore<PluginInstanceChild *> pluginInstance(sCurrentPluginInstance);
if (event.event == WM_IME_STARTCOMPOSITION ||
event.event == WM_IME_COMPOSITION ||
event.event == WM_KILLFOCUS) {
sCurrentPluginInstance = this;
}
MessageLoop* loop = MessageLoop::current();
AutoRestore<bool> modalLoop(loop->os_modal_loop());

View File

@ -305,6 +305,7 @@ private:
void HookSetWindowLongPtr();
void SetUnityHooks();
void ClearUnityHooks();
void InitImm32Hook();
static inline bool SetWindowLongHookCheck(HWND hWnd,
int nIndex,
LONG_PTR newLong);
@ -350,6 +351,15 @@ private:
LONG newLong);
#endif
static HIMC WINAPI ImmGetContextProc(HWND aWND);
static BOOL WINAPI ImmReleaseContextProc(HWND aWND, HIMC aIMC);
static LONG WINAPI ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
LPVOID aBuf, DWORD aLen);
static BOOL WINAPI ImmSetCandidateWindowProc(HIMC hIMC,
LPCANDIDATEFORM plCandidate);
static BOOL WINAPI ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
DWORD aValue);
class FlashThrottleAsyncMsg : public ChildAsyncCall
{
public:

View File

@ -1568,6 +1568,16 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
// We send this in nsPluginFrame just before painting
return SendWindowPosChanged(npremoteevent);
}
case WM_IME_STARTCOMPOSITION:
case WM_IME_COMPOSITION:
case WM_IME_ENDCOMPOSITION:
if (!(mParent->GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
// IME message will be posted on allowed plugins only such as
// Flash. Because if we cannot know that plugin can handle
// IME correctly.
return 0;
}
break;
}
}
@ -2368,6 +2378,50 @@ PluginInstanceParent::Cast(NPP aInstance, PluginAsyncSurrogate** aSurrogate)
return instancePtr;
}
bool
PluginInstanceParent::RecvGetCompositionString(const uint32_t& aIndex,
nsTArray<uint8_t>* aDist,
int32_t* aLength)
{
#if defined(OS_WIN)
nsPluginInstanceOwner* owner = GetOwner();
if (!owner) {
*aLength = IMM_ERROR_GENERAL;
return true;
}
if (!owner->GetCompositionString(aIndex, aDist, aLength)) {
*aLength = IMM_ERROR_NODATA;
}
#endif
return true;
}
bool
PluginInstanceParent::RecvSetCandidateWindow(const int32_t& aX,
const int32_t& aY)
{
#if defined(OS_WIN)
nsPluginInstanceOwner* owner = GetOwner();
if (owner) {
owner->SetCandidateWindow(aX, aY);
}
#endif
return true;
}
bool
PluginInstanceParent::RecvRequestCommitOrCancel(const bool& aCommitted)
{
#if defined(OS_WIN)
nsPluginInstanceOwner* owner = GetOwner();
if (owner) {
owner->RequestCommitOrCancel(aCommitted);
}
#endif
return true;
}
void
PluginInstanceParent::RecordDrawingModel()
{

View File

@ -352,6 +352,16 @@ public:
static PluginInstanceParent* Cast(NPP instance,
PluginAsyncSurrogate** aSurrogate = nullptr);
// for IME hook
virtual bool
RecvGetCompositionString(const uint32_t& aIndex,
nsTArray<uint8_t>* aBuffer,
int32_t* aLength) override;
virtual bool
RecvSetCandidateWindow(const int32_t& aX, const int32_t& aY) override;
virtual bool
RecvRequestCommitOrCancel(const bool& aCommitted) override;
private:
// Create an appropriate platform surface for a background of size
// |aSize|. Return true if successful.

View File

@ -34,6 +34,7 @@ int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
quirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE;
quirks |= QUIRK_WINLESS_HOOK_IME;
#endif
}
@ -62,6 +63,12 @@ int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
}
#endif
#ifdef OS_WIN
if (specialType == nsPluginHost::eSpecialType_Test) {
quirks |= QUIRK_WINLESS_HOOK_IME;
}
#endif
return quirks;
}

View File

@ -58,6 +58,8 @@ enum PluginQuirks {
QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN = 1 << 11,
// Win: Addresses a Unity bug with mouse capture.
QUIRK_UNITY_FIXUP_MOUSE_CAPTURE = 1 << 12,
// Win: Hook IMM32 API to handle IME event on windowless plugin
QUIRK_WINLESS_HOOK_IME = 1 << 13,
};
int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,

View File

@ -1404,5 +1404,15 @@ PuppetWidget::GetCurrentWidgetListener()
return mAttachedWidgetListener;
}
void
PuppetWidget::SetCandidateWindowForPlugin(int32_t aX, int32_t aY)
{
if (!mTabChild) {
return;
}
mTabChild->SendSetCandidateWindowForPlugin(aX, aY);
}
} // namespace widget
} // namespace mozilla

View File

@ -254,6 +254,9 @@ public:
virtual uint32_t GetMaxTouchPoints() const override;
virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
virtual void SetCandidateWindowForPlugin(int32_t aX, int32_t aY) override;
protected:
virtual nsresult NotifyIMEInternal(
const IMENotification& aIMENotification) override;

View File

@ -266,6 +266,16 @@ public:
}
return false;
}
uint32_t GetCaretPosition() const
{
for (const TextRange& range : *this) {
if (range.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
return range.mStartOffset;
}
}
return UINT32_MAX;
}
};
} // namespace mozilla

View File

@ -217,6 +217,9 @@ public:
{ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD SetPluginFocused(bool& aFocused) override
{ return NS_ERROR_NOT_IMPLEMENTED; }
virtual void SetCandidateWindowForPlugin(int32_t aX,
int32_t aY) override
{ }
NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) override { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
NativeKeyBindingsType aType,

View File

@ -133,8 +133,8 @@ typedef void* nsNativeWidget;
#endif
#define NS_IWIDGET_IID \
{ 0xaaa79c8d, 0xc99d, 0x4fe1, \
{ 0xa5, 0x11, 0xd3, 0xeb, 0xb1, 0x61, 0x9e, 0x26 } }
{ 0xa3db64d2, 0x5a73, 0x425f, \
{ 0x9b, 0xb0, 0x57, 0x7f, 0xe5, 0x56, 0x43, 0x15 } }
/*
* Window shadow styles
@ -1799,6 +1799,11 @@ public:
return GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN;
}
/**
* Set IME candidate window position by windowless plugin.
*/
virtual void SetCandidateWindowForPlugin(int32_t aX, int32_t aY) = 0;
/*
* Notifies the input context changes.
*/

View File

@ -2749,6 +2749,14 @@ IMMHandler::OnKeyDownEvent(nsWindow* aWindow,
}
}
// static
void
IMMHandler::SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm)
{
IMEContext context(aWindow);
ImmSetCandidateWindow(context.get(), aForm);
}
/******************************************************************************
* IMMHandler::Selection
******************************************************************************/

View File

@ -150,6 +150,7 @@ public:
// IME. Otherwise, NS_OK.
static nsresult OnMouseButtonEvent(nsWindow* aWindow,
const IMENotification& aIMENotification);
static void SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm);
protected:
static void EnsureHandlerInstance();

View File

@ -948,5 +948,16 @@ IMEHandler::GetOnScreenKeyboardWindow()
return nullptr;
}
// static
void
IMEHandler::SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm)
{
if (!sPluginHasFocus) {
return;
}
IMMHandler::SetCandidateWindow(aWindow, aForm);
}
} // namespace widget
} // namespace mozilla

View File

@ -103,6 +103,11 @@ public:
*/
static void InitInputContext(nsWindow* aWindow, InputContext& aInputContext);
/*
* For windowless plugin helper.
*/
static void SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm);
#ifdef DEBUG
/**
* Returns true when current keyboard layout has IME. Otherwise, false.

View File

@ -7751,6 +7751,18 @@ nsWindow::ComputeShouldAccelerate()
return nsBaseWidget::ComputeShouldAccelerate();
}
void
nsWindow::SetCandidateWindowForPlugin(int32_t aX, int32_t aY)
{
CANDIDATEFORM form;
form.dwIndex = 0;
form.dwStyle = CFS_CANDIDATEPOS;
form.ptCurrentPos.x = aX;
form.ptCurrentPos.y = aY;
IMEHandler::SetCandidateWindow(this, &form);
}
/**************************************************************
**************************************************************
**

View File

@ -297,6 +297,9 @@ public:
const IMEContext& DefaultIMC() const { return mDefaultIMC; }
virtual void SetCandidateWindowForPlugin(int32_t aX,
int32_t aY) override;
protected:
virtual ~nsWindow();