/* ***** 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 * Jim Mathies * * ***** END LICENSE BLOCK ***** */ #include "nptest_platform.h" #include #include #include #include #include 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 (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(&bitmapheader), 0, reinterpret_cast(&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(::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(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(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(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(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; }