summaryrefslogtreecommitdiffstats
path: root/dom/plugins/test/testplugin/nptest_windows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/test/testplugin/nptest_windows.cpp')
-rw-r--r--dom/plugins/test/testplugin/nptest_windows.cpp878
1 files changed, 878 insertions, 0 deletions
diff --git a/dom/plugins/test/testplugin/nptest_windows.cpp b/dom/plugins/test/testplugin/nptest_windows.cpp
new file mode 100644
index 000000000..8b02872e9
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -0,0 +1,878 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ * Jim Mathies <jmathies@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+
+#include <windows.h>
+#include <windowsx.h>
+#include <stdio.h>
+
+#include <d3d10_1.h>
+#include <d2d1.h>
+
+using namespace std;
+
+void SetSubclass(HWND hWnd, InstanceData* instanceData);
+void ClearSubclass(HWND hWnd);
+LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+struct _PlatformData {
+ HWND childWindow;
+ IDXGIAdapter1 *adapter;
+ ID3D10Device1 *device;
+ ID3D10Texture2D *frontBuffer;
+ ID3D10Texture2D *backBuffer;
+ ID2D1Factory *d2d1Factory;
+};
+
+bool
+pluginSupportsWindowMode()
+{
+ return true;
+}
+
+bool
+pluginSupportsWindowlessMode()
+{
+ return true;
+}
+
+NPError
+pluginInstanceInit(InstanceData* instanceData)
+{
+ NPP npp = instanceData->npp;
+
+ instanceData->platformData = static_cast<PlatformData*>
+ (NPN_MemAlloc(sizeof(PlatformData)));
+ if (!instanceData->platformData)
+ return NPERR_OUT_OF_MEMORY_ERROR;
+
+ instanceData->platformData->childWindow = nullptr;
+ instanceData->platformData->device = nullptr;
+ instanceData->platformData->frontBuffer = nullptr;
+ instanceData->platformData->backBuffer = nullptr;
+ instanceData->platformData->adapter = nullptr;
+ instanceData->platformData->d2d1Factory = nullptr;
+ return NPERR_NO_ERROR;
+}
+
+static inline bool
+openSharedTex2D(ID3D10Device* device, HANDLE handle, ID3D10Texture2D** out)
+{
+ HRESULT hr = device->OpenSharedResource(handle, __uuidof(ID3D10Texture2D), (void**)out);
+ if (FAILED(hr) || !*out) {
+ return false;
+ }
+ return true;
+}
+
+// This is overloaded in d2d1.h so we can't use decltype().
+typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType,
+ REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ void **factory
+);
+
+static IDXGIAdapter1*
+FindDXGIAdapter(NPP npp, IDXGIFactory1* factory)
+{
+ DXGI_ADAPTER_DESC preferred;
+ if (NPN_GetValue(npp, NPNVpreferredDXGIAdapter, &preferred) != NPERR_NO_ERROR) {
+ return nullptr;
+ }
+
+ UINT index = 0;
+ for (;;) {
+ IDXGIAdapter1* adapter = nullptr;
+ if (FAILED(factory->EnumAdapters1(index, &adapter)) || !adapter) {
+ return nullptr;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (SUCCEEDED(adapter->GetDesc(&desc)) &&
+ desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
+ desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
+ desc.VendorId == preferred.VendorId &&
+ desc.DeviceId == preferred.DeviceId)
+ {
+ return adapter;
+ }
+
+ adapter->Release();
+ index++;
+ }
+}
+
+// Note: we leak modules since we need them anyway.
+bool
+setupDxgiSurfaces(NPP npp, InstanceData* instanceData)
+{
+ HMODULE dxgi = LoadLibraryA("dxgi.dll");
+ if (!dxgi) {
+ return false;
+ }
+ decltype(CreateDXGIFactory1)* createDXGIFactory1 =
+ (decltype(CreateDXGIFactory1)*)GetProcAddress(dxgi, "CreateDXGIFactory1");
+ if (!createDXGIFactory1) {
+ return false;
+ }
+
+ IDXGIFactory1* factory1 = nullptr;
+ HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory1);
+ if (FAILED(hr) || !factory1) {
+ return false;
+ }
+
+ instanceData->platformData->adapter = FindDXGIAdapter(npp, factory1);
+ if (!instanceData->platformData->adapter) {
+ return false;
+ }
+
+ HMODULE d3d10 = LoadLibraryA("d3d10_1.dll");
+ if (!d3d10) {
+ return false;
+ }
+
+ decltype(D3D10CreateDevice1)* createDevice =
+ (decltype(D3D10CreateDevice1)*)GetProcAddress(d3d10, "D3D10CreateDevice1");
+ if (!createDevice) {
+ return false;
+ }
+
+
+ hr = createDevice(
+ instanceData->platformData->adapter,
+ D3D10_DRIVER_TYPE_HARDWARE, nullptr,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_1,
+ D3D10_1_SDK_VERSION, &instanceData->platformData->device);
+ if (FAILED(hr) || !instanceData->platformData->device) {
+ return false;
+ }
+
+ if (!openSharedTex2D(instanceData->platformData->device,
+ instanceData->frontBuffer->sharedHandle,
+ &instanceData->platformData->frontBuffer))
+ {
+ return false;
+ }
+ if (!openSharedTex2D(instanceData->platformData->device,
+ instanceData->backBuffer->sharedHandle,
+ &instanceData->platformData->backBuffer))
+ {
+ return false;
+ }
+
+ HMODULE d2d1 = LoadLibraryA("D2d1.dll");
+ if (!d2d1) {
+ return false;
+ }
+ auto d2d1CreateFactory = (D2D1CreateFactoryFunc)GetProcAddress(d2d1, "D2D1CreateFactory");
+ if (!d2d1CreateFactory) {
+ return false;
+ }
+
+ D2D1_FACTORY_OPTIONS options;
+ options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+
+ hr = d2d1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory),
+ &options,
+ (void**)&instanceData->platformData->d2d1Factory);
+ if (FAILED(hr) || !instanceData->platformData->d2d1Factory) {
+ return false;
+ }
+
+ return true;
+}
+
+void
+drawDxgiBitmapColor(InstanceData* instanceData)
+{
+ NPP npp = instanceData->npp;
+
+ HRESULT hr;
+
+ IDXGISurface* surface = nullptr;
+ hr = instanceData->platformData->backBuffer->QueryInterface(
+ __uuidof(IDXGISurface), (void **)&surface);
+ if (FAILED(hr) || !surface) {
+ return;
+ }
+
+ D2D1_RENDER_TARGET_PROPERTIES props =
+ D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
+
+ ID2D1RenderTarget* target = nullptr;
+ hr = instanceData->platformData->d2d1Factory->CreateDxgiSurfaceRenderTarget(
+ surface,
+ &props,
+ &target);
+ if (FAILED(hr) || !target) {
+ surface->Release();
+ return;
+ }
+
+ IDXGIKeyedMutex* mutex = nullptr;
+ hr = instanceData->platformData->backBuffer->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&mutex);
+ if (mutex) {
+ mutex->AcquireSync(0, 0);
+ }
+
+ target->BeginDraw();
+
+ unsigned char subpixels[4];
+ memcpy(subpixels,
+ &instanceData->scriptableObject->drawColor,
+ sizeof(subpixels));
+
+ auto rect = D2D1::RectF(
+ 0, 0,
+ instanceData->backBuffer->size.width,
+ instanceData->backBuffer->size.height);
+ auto color = D2D1::ColorF(
+ float(subpixels[3] * subpixels[2]) / 0xFF,
+ float(subpixels[3] * subpixels[1]) / 0xFF,
+ float(subpixels[3] * subpixels[0]) / 0xFF,
+ float(subpixels[3]) / 0xff);
+
+ ID2D1SolidColorBrush* brush = nullptr;
+ hr = target->CreateSolidColorBrush(color, &brush);
+ if (SUCCEEDED(hr) && brush) {
+ target->FillRectangle(rect, brush);
+ brush->Release();
+ brush = nullptr;
+ }
+ hr = target->EndDraw();
+
+ if (mutex) {
+ mutex->ReleaseSync(0);
+ mutex->Release();
+ mutex = nullptr;
+ }
+
+ target->Release();
+ surface->Release();
+ target = nullptr;
+ surface = nullptr;
+
+ NPN_SetCurrentAsyncSurface(npp, instanceData->backBuffer, NULL);
+ std::swap(instanceData->backBuffer, instanceData->frontBuffer);
+ std::swap(instanceData->platformData->backBuffer,
+ instanceData->platformData->frontBuffer);
+}
+
+void
+pluginInstanceShutdown(InstanceData* instanceData)
+{
+ PlatformData *pd = instanceData->platformData;
+ if (pd->frontBuffer) {
+ pd->frontBuffer->Release();
+ }
+ if (pd->backBuffer) {
+ pd->backBuffer->Release();
+ }
+ if (pd->d2d1Factory) {
+ pd->d2d1Factory->Release();
+ }
+ if (pd->device) {
+ pd->device->Release();
+ }
+ if (pd->adapter) {
+ pd->adapter->Release();
+ }
+ NPN_MemFree(instanceData->platformData);
+ instanceData->platformData = 0;
+}
+
+void
+pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow)
+{
+ instanceData->window = *newWindow;
+}
+
+#define CHILD_WIDGET_SIZE 10
+
+void
+pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
+{
+ HWND hWnd = (HWND)instanceData->window.window;
+ if (oldWindow) {
+ // chrashtests/539897-1.html excercises this code
+ HWND hWndOld = (HWND)oldWindow;
+ ClearSubclass(hWndOld);
+ if (instanceData->platformData->childWindow) {
+ ::DestroyWindow(instanceData->platformData->childWindow);
+ }
+ }
+
+ SetSubclass(hWnd, instanceData);
+
+ instanceData->platformData->childWindow =
+ ::CreateWindowW(L"SCROLLBAR", L"Dummy child window",
+ WS_CHILD, 0, 0, CHILD_WIDGET_SIZE, CHILD_WIDGET_SIZE, hWnd, nullptr,
+ nullptr, nullptr);
+}
+
+static void
+drawToDC(InstanceData* instanceData, HDC dc,
+ int x, int y, int width, int height)
+{
+ switch (instanceData->scriptableObject->drawMode) {
+ case DM_DEFAULT:
+ {
+ const RECT fill = { x, y, x + width, y + height };
+
+ int oldBkMode = ::SetBkMode(dc, TRANSPARENT);
+ HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
+ if (brush) {
+ ::FillRect(dc, &fill, brush);
+ ::DeleteObject(brush);
+ }
+ if (width > 6 && height > 6) {
+ brush = ::CreateSolidBrush(RGB(192, 192, 192));
+ if (brush) {
+ RECT inset = { x + 3, y + 3, x + width - 3, y + height - 3 };
+ ::FillRect(dc, &inset, brush);
+ ::DeleteObject(brush);
+ }
+ }
+
+ const char* uaString = NPN_UserAgent(instanceData->npp);
+ if (uaString && width > 10 && height > 10) {
+ HFONT font =
+ ::CreateFontA(20, 0, 0, 0, 400, FALSE, FALSE, FALSE,
+ DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS, 5, // CLEARTYPE_QUALITY
+ DEFAULT_PITCH, "Arial");
+ if (font) {
+ HFONT oldFont = (HFONT)::SelectObject(dc, font);
+ RECT inset = { x + 5, y + 5, x + width - 5, y + height - 5 };
+ ::DrawTextA(dc, uaString, -1, &inset,
+ DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK);
+ ::SelectObject(dc, oldFont);
+ ::DeleteObject(font);
+ }
+ }
+ ::SetBkMode(dc, oldBkMode);
+ }
+ break;
+
+ case DM_SOLID_COLOR:
+ {
+ HDC offscreenDC = ::CreateCompatibleDC(dc);
+ if (!offscreenDC)
+ return;
+
+ const BITMAPV4HEADER bitmapheader = {
+ sizeof(BITMAPV4HEADER),
+ width,
+ height,
+ 1, // planes
+ 32, // bits
+ BI_BITFIELDS,
+ 0, // unused size
+ 0, 0, // unused metrics
+ 0, 0, // unused colors used/important
+ 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, // ARGB masks
+ };
+ uint32_t *pixelData;
+ HBITMAP offscreenBitmap =
+ ::CreateDIBSection(dc, reinterpret_cast<const BITMAPINFO*>(&bitmapheader),
+ 0, reinterpret_cast<void**>(&pixelData), 0, 0);
+ if (!offscreenBitmap)
+ return;
+
+ uint32_t rgba = instanceData->scriptableObject->drawColor;
+ unsigned int alpha = ((rgba & 0xFF000000) >> 24);
+ BYTE r = ((rgba & 0xFF0000) >> 16);
+ BYTE g = ((rgba & 0xFF00) >> 8);
+ BYTE b = (rgba & 0xFF);
+
+ // Windows expects premultiplied
+ r = BYTE(float(alpha * r) / 0xFF);
+ g = BYTE(float(alpha * g) / 0xFF);
+ b = BYTE(float(alpha * b) / 0xFF);
+ uint32_t premultiplied =
+ (alpha << 24) + (r << 16) + (g << 8) + b;
+
+ for (uint32_t* lastPixel = pixelData + width * height;
+ pixelData < lastPixel;
+ ++pixelData)
+ *pixelData = premultiplied;
+
+ ::SelectObject(offscreenDC, offscreenBitmap);
+ BLENDFUNCTION blendFunc;
+ blendFunc.BlendOp = AC_SRC_OVER;
+ blendFunc.BlendFlags = 0;
+ blendFunc.SourceConstantAlpha = 255;
+ blendFunc.AlphaFormat = AC_SRC_ALPHA;
+ ::AlphaBlend(dc, x, y, width, height, offscreenDC, 0, 0, width, height,
+ blendFunc);
+
+ ::DeleteObject(offscreenDC);
+ ::DeleteObject(offscreenBitmap);
+ }
+ break;
+ }
+}
+
+void
+pluginDraw(InstanceData* instanceData)
+{
+ NPP npp = instanceData->npp;
+ if (!npp)
+ return;
+
+ HDC hdc = nullptr;
+ PAINTSTRUCT ps;
+
+ notifyDidPaint(instanceData);
+
+ if (instanceData->hasWidget)
+ hdc = ::BeginPaint((HWND)instanceData->window.window, &ps);
+ else
+ hdc = (HDC)instanceData->window.window;
+
+ if (hdc == nullptr)
+ return;
+
+ // Push the browser's hdc on the resource stack. If this test plugin is windowless,
+ // we share the drawing surface with the rest of the browser.
+ int savedDCID = SaveDC(hdc);
+
+ // When we have a widget, window.x/y are meaningless since our widget
+ // is always positioned correctly and we just draw into it at 0,0.
+ int x = instanceData->hasWidget ? 0 : instanceData->window.x;
+ int y = instanceData->hasWidget ? 0 : instanceData->window.y;
+ int width = instanceData->window.width;
+ int height = instanceData->window.height;
+ drawToDC(instanceData, hdc, x, y, width, height);
+
+ // Pop our hdc changes off the resource stack
+ RestoreDC(hdc, savedDCID);
+
+ if (instanceData->hasWidget)
+ ::EndPaint((HWND)instanceData->window.window, &ps);
+}
+
+/* script interface */
+
+int32_t
+pluginGetEdge(InstanceData* instanceData, RectEdge edge)
+{
+ if (!instanceData || !instanceData->hasWidget)
+ return NPTEST_INT32_ERROR;
+
+ // Get the plugin client rect in screen coordinates
+ RECT rect = {0};
+ if (!::GetClientRect((HWND)instanceData->window.window, &rect))
+ return NPTEST_INT32_ERROR;
+ ::MapWindowPoints((HWND)instanceData->window.window, nullptr,
+ (LPPOINT)&rect, 2);
+
+ // Get the toplevel window frame rect in screen coordinates
+ HWND rootWnd = ::GetAncestor((HWND)instanceData->window.window, GA_ROOT);
+ if (!rootWnd)
+ return NPTEST_INT32_ERROR;
+ RECT rootRect;
+ if (!::GetWindowRect(rootWnd, &rootRect))
+ return NPTEST_INT32_ERROR;
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return rect.left - rootRect.left;
+ case EDGE_TOP:
+ return rect.top - rootRect.top;
+ case EDGE_RIGHT:
+ return rect.right - rootRect.left;
+ case EDGE_BOTTOM:
+ return rect.bottom - rootRect.top;
+ }
+
+ return NPTEST_INT32_ERROR;
+}
+
+static BOOL
+getWindowRegion(HWND wnd, HRGN rgn)
+{
+ if (::GetWindowRgn(wnd, rgn) != ERROR)
+ return TRUE;
+
+ RECT clientRect;
+ if (!::GetClientRect(wnd, &clientRect))
+ return FALSE;
+ return ::SetRectRgn(rgn, 0, 0, clientRect.right, clientRect.bottom);
+}
+
+static RGNDATA*
+computeClipRegion(InstanceData* instanceData)
+{
+ HWND wnd = (HWND)instanceData->window.window;
+ HRGN rgn = ::CreateRectRgn(0, 0, 0, 0);
+ if (!rgn)
+ return nullptr;
+ HRGN ancestorRgn = ::CreateRectRgn(0, 0, 0, 0);
+ if (!ancestorRgn) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+ if (!getWindowRegion(wnd, rgn)) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+
+ HWND ancestor = wnd;
+ for (;;) {
+ ancestor = ::GetAncestor(ancestor, GA_PARENT);
+ if (!ancestor || ancestor == ::GetDesktopWindow()) {
+ ::DeleteObject(ancestorRgn);
+
+ DWORD size = ::GetRegionData(rgn, 0, nullptr);
+ if (!size) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+
+ HANDLE heap = ::GetProcessHeap();
+ RGNDATA* data = static_cast<RGNDATA*>(::HeapAlloc(heap, 0, size));
+ if (!data) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+ DWORD result = ::GetRegionData(rgn, size, data);
+ ::DeleteObject(rgn);
+ if (!result) {
+ ::HeapFree(heap, 0, data);
+ return nullptr;
+ }
+
+ return data;
+ }
+
+ if (!getWindowRegion(ancestor, ancestorRgn)) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return 0;
+ }
+
+ POINT pt = { 0, 0 };
+ ::MapWindowPoints(ancestor, wnd, &pt, 1);
+ if (::OffsetRgn(ancestorRgn, pt.x, pt.y) == ERROR ||
+ ::CombineRgn(rgn, rgn, ancestorRgn, RGN_AND) == ERROR) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return 0;
+ }
+ }
+}
+
+int32_t
+pluginGetClipRegionRectCount(InstanceData* instanceData)
+{
+ RGNDATA* data = computeClipRegion(instanceData);
+ if (!data)
+ return NPTEST_INT32_ERROR;
+
+ int32_t result = data->rdh.nCount;
+ ::HeapFree(::GetProcessHeap(), 0, data);
+ return result;
+}
+
+static int32_t
+addOffset(LONG coord, int32_t offset)
+{
+ if (offset == NPTEST_INT32_ERROR)
+ return NPTEST_INT32_ERROR;
+ return coord + offset;
+}
+
+int32_t
+pluginGetClipRegionRectEdge(InstanceData* instanceData,
+ int32_t rectIndex, RectEdge edge)
+{
+ RGNDATA* data = computeClipRegion(instanceData);
+ if (!data)
+ return NPTEST_INT32_ERROR;
+
+ HANDLE heap = ::GetProcessHeap();
+ if (rectIndex >= int32_t(data->rdh.nCount)) {
+ ::HeapFree(heap, 0, data);
+ return NPTEST_INT32_ERROR;
+ }
+
+ RECT rect = reinterpret_cast<RECT*>(data->Buffer)[rectIndex];
+ ::HeapFree(heap, 0, data);
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return addOffset(rect.left, pluginGetEdge(instanceData, EDGE_LEFT));
+ case EDGE_TOP:
+ return addOffset(rect.top, pluginGetEdge(instanceData, EDGE_TOP));
+ case EDGE_RIGHT:
+ return addOffset(rect.right, pluginGetEdge(instanceData, EDGE_LEFT));
+ case EDGE_BOTTOM:
+ return addOffset(rect.bottom, pluginGetEdge(instanceData, EDGE_TOP));
+ }
+
+ 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<void*>(CreateWindowW(L"SWFlash_PlaceholderX", L"", WS_CHILD, 0,
+ 0, 0, 0, HWND_MESSAGE, NULL,
+ GetModuleHandleW(NULL), NULL));
+}
+
+/* windowless plugin events */
+
+static bool
+handleEventInternal(InstanceData* instanceData, NPEvent* pe, LRESULT* result)
+{
+ switch ((UINT)pe->event) {
+ case WM_PAINT:
+ pluginDraw(instanceData);
+ return true;
+
+ case WM_MOUSEACTIVATE:
+ if (instanceData->hasWidget) {
+ ::SetFocus((HWND)instanceData->window.window);
+ *result = MA_ACTIVATEANDEAT;
+ return true;
+ }
+ return false;
+
+ case WM_MOUSEWHEEL:
+ return true;
+
+ case WM_WINDOWPOSCHANGED: {
+ WINDOWPOS* pPos = (WINDOWPOS*)pe->lParam;
+ instanceData->winX = instanceData->winY = 0;
+ if (pPos) {
+ instanceData->winX = pPos->x;
+ instanceData->winY = pPos->y;
+ return true;
+ }
+ return false;
+ }
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP: {
+ int x = instanceData->hasWidget ? 0 : instanceData->winX;
+ int y = instanceData->hasWidget ? 0 : instanceData->winY;
+ instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x;
+ instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y;
+ if ((UINT)pe->event == WM_LBUTTONUP) {
+ instanceData->mouseUpEventCount++;
+ }
+ return true;
+ }
+
+ case WM_KEYDOWN:
+ instanceData->lastKeyText.erase();
+ *result = 0;
+ return true;
+
+ case WM_CHAR: {
+ *result = 0;
+ wchar_t uniChar = static_cast<wchar_t>(pe->wParam);
+ if (!uniChar) {
+ return true;
+ }
+ char utf8Char[6];
+ int len =
+ ::WideCharToMultiByte(CP_UTF8, 0, &uniChar, 1, utf8Char, 6,
+ nullptr, nullptr);
+ if (len == 0 || len > 6) {
+ return true;
+ }
+ instanceData->lastKeyText.append(utf8Char, len);
+ 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;
+ }
+}
+
+int16_t
+pluginHandleEvent(InstanceData* instanceData, void* event)
+{
+ NPEvent* pe = (NPEvent*)event;
+
+ if (pe == nullptr || instanceData == nullptr ||
+ instanceData->window.type != NPWindowTypeDrawable)
+ return 0;
+
+ LRESULT result = 0;
+ return handleEventInternal(instanceData, pe, &result);
+}
+
+/* windowed plugin events */
+
+LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC wndProc = (WNDPROC)GetProp(hWnd, "MozillaWndProc");
+ if (!wndProc)
+ return 0;
+ InstanceData* pInstance = (InstanceData*)GetProp(hWnd, "InstanceData");
+ if (!pInstance)
+ return 0;
+
+ NPEvent event = { static_cast<uint16_t>(uMsg), wParam, lParam };
+
+ LRESULT result = 0;
+ if (handleEventInternal(pInstance, &event, &result))
+ return result;
+
+ if (uMsg == WM_CLOSE) {
+ ClearSubclass((HWND)pInstance->window.window);
+ }
+
+ return CallWindowProc(wndProc, hWnd, uMsg, wParam, lParam);
+}
+
+void
+ClearSubclass(HWND hWnd)
+{
+ if (GetProp(hWnd, "MozillaWndProc")) {
+ ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)GetProp(hWnd, "MozillaWndProc"));
+ RemoveProp(hWnd, "MozillaWndProc");
+ RemoveProp(hWnd, "InstanceData");
+ }
+}
+
+void
+SetSubclass(HWND hWnd, InstanceData* instanceData)
+{
+ // Subclass the plugin window so we can handle our own windows events.
+ SetProp(hWnd, "InstanceData", (HANDLE)instanceData);
+ WNDPROC origProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ SetProp(hWnd, "MozillaWndProc", (HANDLE)origProc);
+}
+
+static void checkEquals(int a, int b, const char* msg, string& error)
+{
+ if (a == b) {
+ return;
+ }
+
+ error.append(msg);
+ char buf[100];
+ sprintf(buf, " (got %d, expected %d)\n", a, b);
+ error.append(buf);
+}
+
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error)
+{
+ if (instanceData->platformData->childWindow) {
+ RECT childRect;
+ ::GetWindowRect(instanceData->platformData->childWindow, &childRect);
+ RECT ourRect;
+ HWND hWnd = (HWND)instanceData->window.window;
+ ::GetWindowRect(hWnd, &ourRect);
+ checkEquals(childRect.left, ourRect.left, "Child widget left", error);
+ checkEquals(childRect.top, ourRect.top, "Child widget top", error);
+ checkEquals(childRect.right, childRect.left + CHILD_WIDGET_SIZE, "Child widget width", error);
+ checkEquals(childRect.bottom, childRect.top + CHILD_WIDGET_SIZE, "Child widget height", error);
+ }
+}
+
+bool pluginNativeWidgetIsVisible(InstanceData* instanceData)
+{
+ HWND hWnd = (HWND)instanceData->window.window;
+ wchar_t className[60];
+ if (::GetClassNameW(hWnd, className, sizeof(className) / sizeof(char16_t)) &&
+ !wcsicmp(className, L"GeckoPluginWindow")) {
+ return ::IsWindowVisible(hWnd);
+ }
+ // something isn't right, fail the check
+ return false;
+}