diff --git a/dom/plugins/test/mochitest/mochitest.ini b/dom/plugins/test/mochitest/mochitest.ini
index 6494bc7ac9b2..6ed983d34c6b 100644
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -116,6 +116,8 @@ skip-if = toolkit != "cocoa"
[test_twostreams.html]
[test_windowed_invalidate.html]
skip-if = os != "win"
+[test_windowless_ime.html]
+skip-if = os != "win"
[test_visibility.html]
skip-if = toolkit == "cocoa"
[test_zero_opacity.html]
diff --git a/dom/plugins/test/mochitest/test_windowless_ime.html b/dom/plugins/test/mochitest/test_windowless_ime.html
new file mode 100644
index 000000000000..8a1264fdc2a9
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_windowless_ime.html
@@ -0,0 +1,46 @@
+
+
+
+
+ Test #1 for Bug 539565
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dom/plugins/test/testplugin/nptest.cpp b/dom/plugins/test/testplugin/nptest.cpp
index 6e3dc640a9b0..6651ce349681 100644
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -173,6 +173,7 @@ static bool startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t
static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool nativeWidgetIsVisible(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getLastCompositionText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static const NPUTF8* sPluginMethodIdentifierNames[] = {
"npnEvaluateTest",
@@ -244,6 +245,7 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = {
"stopAudioPlayback",
"audioMuted",
"nativeWidgetIsVisible",
+ "getLastCompositionText",
};
static NPIdentifier sPluginMethodIdentifiers[MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames)];
static const ScriptableFunction sPluginMethodFunctions[] = {
@@ -316,6 +318,7 @@ static const ScriptableFunction sPluginMethodFunctions[] = {
stopAudioPlayback,
getAudioMuted,
nativeWidgetIsVisible,
+ getLastCompositionText,
};
static_assert(MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
@@ -852,6 +855,7 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char*
instanceData->asyncDrawing = AD_NONE;
instanceData->frontBuffer = nullptr;
instanceData->backBuffer = nullptr;
+ instanceData->placeholderWnd = nullptr;
instance->pdata = instanceData;
TestNPObject* scriptableObject = (TestNPObject*)NPN_CreateObject(instance, &sNPClass);
@@ -3514,6 +3518,26 @@ nativeWidgetIsVisible(NPObject* npobj, const NPVariant* args,
}
#endif
+bool
+getLastCompositionText(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result)
+{
+#ifdef XP_WIN
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast(npobj)->npp;
+ InstanceData* id = static_cast(npp->pdata);
+ char *outval = NPN_StrDup(id->lastComposition.c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ return true;
+#else
+ // XXX not implemented
+ return false;
+#endif
+}
+
bool
callOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
{
diff --git a/dom/plugins/test/testplugin/nptest.h b/dom/plugins/test/testplugin/nptest.h
index 12e427a5e60f..2fbc2d4b437c 100644
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -156,6 +156,8 @@ typedef struct InstanceData {
AsyncDrawing asyncDrawing;
NPAsyncSurface *frontBuffer;
NPAsyncSurface *backBuffer;
+ std::string lastComposition;
+ void* placeholderWnd;
} InstanceData;
void notifyDidPaint(InstanceData* instanceData);
diff --git a/dom/plugins/test/testplugin/nptest_windows.cpp b/dom/plugins/test/testplugin/nptest_windows.cpp
index 3d807bc532ec..8b02872e9540 100644
--- a/dom/plugins/test/testplugin/nptest_windows.cpp
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -654,6 +654,29 @@ pluginGetClipRegionRectEdge(InstanceData* instanceData,
return NPTEST_INT32_ERROR;
}
+static
+void
+createDummyWindowForIME(InstanceData* instanceData)
+{
+ WNDCLASSW wndClass;
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = DefWindowProcW;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = GetModuleHandleW(NULL);
+ wndClass.hIcon = nullptr;
+ wndClass.hCursor = nullptr;
+ wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = L"SWFlash_PlaceholderX";
+ RegisterClassW(&wndClass);
+
+ instanceData->placeholderWnd =
+ static_cast(CreateWindowW(L"SWFlash_PlaceholderX", L"", WS_CHILD, 0,
+ 0, 0, 0, HWND_MESSAGE, NULL,
+ GetModuleHandleW(NULL), NULL));
+}
+
/* windowless plugin events */
static bool
@@ -725,6 +748,35 @@ handleEventInternal(InstanceData* instanceData, NPEvent* pe, LRESULT* result)
return true;
}
+ case WM_IME_STARTCOMPOSITION:
+ instanceData->lastComposition.erase();
+ if (!instanceData->placeholderWnd) {
+ createDummyWindowForIME(instanceData);
+ }
+ return true;
+
+ case WM_IME_ENDCOMPOSITION:
+ instanceData->lastComposition.erase();
+ return true;
+
+ case WM_IME_COMPOSITION: {
+ if (pe->lParam & GCS_COMPSTR) {
+ HIMC hIMC = ImmGetContext((HWND)instanceData->placeholderWnd);
+ if (!hIMC) {
+ return false;
+ }
+ WCHAR compStr[256];
+ LONG len = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, compStr,
+ 256 * sizeof(WCHAR));
+ CHAR buffer[256];
+ len = ::WideCharToMultiByte(CP_UTF8, 0, compStr, len / sizeof(WCHAR),
+ buffer, 256, nullptr, nullptr);
+ instanceData->lastComposition.append(buffer, len);
+ ::ImmReleaseContext((HWND)instanceData->placeholderWnd, hIMC);
+ }
+ return true;
+ }
+
default:
return false;
}
diff --git a/dom/plugins/test/testplugin/testplugin.mozbuild b/dom/plugins/test/testplugin/testplugin.mozbuild
index e8e1a7615cce..86e3fc24a7c9 100644
--- a/dom/plugins/test/testplugin/testplugin.mozbuild
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -38,6 +38,7 @@ elif toolkit == 'windows':
]
OS_LIBS += [
'msimg32',
+ 'imm32'
]
# must link statically with the CRT; nptest isn't Gecko code