diff options
Diffstat (limited to 'dom/plugins/ipc/hangui/PluginHangUIChild.cpp')
-rw-r--r-- | dom/plugins/ipc/hangui/PluginHangUIChild.cpp | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/dom/plugins/ipc/hangui/PluginHangUIChild.cpp b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp new file mode 100644 index 000000000..c1730a207 --- /dev/null +++ b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp @@ -0,0 +1,425 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PluginHangUI.h" + +#include "PluginHangUIChild.h" +#include "HangUIDlg.h" + +#include <assert.h> +#include <commctrl.h> +#include <windowsx.h> +#include <algorithm> +#include <sstream> +#include <vector> + +namespace mozilla { +namespace plugins { + +struct WinInfo +{ + WinInfo(HWND aHwnd, POINT& aPos, SIZE& aSize) + :hwnd(aHwnd) + { + pos.x = aPos.x; + pos.y = aPos.y; + size.cx = aSize.cx; + size.cy = aSize.cy; + } + HWND hwnd; + POINT pos; + SIZE size; +}; +typedef std::vector<WinInfo> WinInfoVec; + +PluginHangUIChild* PluginHangUIChild::sSelf = nullptr; +const int PluginHangUIChild::kExpectedMinimumArgc = 10; + +PluginHangUIChild::PluginHangUIChild() + : mResponseBits(0), + mParentWindow(nullptr), + mDlgHandle(nullptr), + mMainThread(nullptr), + mParentProcess(nullptr), + mRegWaitProcess(nullptr), + mIPCTimeoutMs(0) +{ +} + +PluginHangUIChild::~PluginHangUIChild() +{ + if (mMainThread) { + CloseHandle(mMainThread); + } + if (mRegWaitProcess) { + UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE); + } + if (mParentProcess) { + CloseHandle(mParentProcess); + } + sSelf = nullptr; +} + +bool +PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[]) +{ + if (aArgc < kExpectedMinimumArgc) { + return false; + } + unsigned int i = 1; + mMessageText = aArgv[i]; + mWindowTitle = aArgv[++i]; + mWaitBtnText = aArgv[++i]; + mKillBtnText = aArgv[++i]; + mNoFutureText = aArgv[++i]; + std::wistringstream issHwnd(aArgv[++i]); + issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow); + if (!issHwnd) { + return false; + } + std::wistringstream issProc(aArgv[++i]); + issProc >> mParentProcess; + if (!issProc) { + return false; + } + // Only set the App User Model ID if it's present in the args + if (wcscmp(aArgv[++i], L"-")) { + HMODULE shell32 = LoadLibrary(L"shell32.dll"); + if (shell32) { + SETAPPUSERMODELID fSetAppUserModelID = (SETAPPUSERMODELID) + GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID"); + if (fSetAppUserModelID) { + fSetAppUserModelID(aArgv[i]); + } + FreeLibrary(shell32); + } + } + std::wistringstream issTimeout(aArgv[++i]); + issTimeout >> mIPCTimeoutMs; + if (!issTimeout) { + return false; + } + + nsresult rv = mMiniShm.Init(this, + std::wstring(aArgv[++i]), + IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs); + if (NS_FAILED(rv)) { + return false; + } + sSelf = this; + return true; +} + +void +PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj) +{ + const PluginHangUICommand* cmd = nullptr; + nsresult rv = aMiniShmObj->GetReadPtr(cmd); + assert(NS_SUCCEEDED(rv)); + bool returnStatus = false; + if (NS_SUCCEEDED(rv)) { + switch (cmd->mCode) { + case PluginHangUICommand::HANGUI_CMD_SHOW: + returnStatus = RecvShow(); + break; + case PluginHangUICommand::HANGUI_CMD_CANCEL: + returnStatus = RecvCancel(); + break; + default: + break; + } + } +} + +// static +INT_PTR CALLBACK +PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, + WPARAM aWParam, LPARAM aLParam) +{ + PluginHangUIChild *self = PluginHangUIChild::sSelf; + if (self) { + return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam); + } + return FALSE; +} + +void +PluginHangUIChild::ResizeButtons() +{ + // Control IDs are specified right-to-left as they appear in the dialog + UINT ids[] = { IDC_STOP, IDC_CONTINUE }; + UINT numIds = sizeof(ids)/sizeof(ids[0]); + + // Pass 1: Compute the ideal size + bool needResizing = false; + SIZE idealSize = {0}; + WinInfoVec winInfo; + for (UINT i = 0; i < numIds; ++i) { + HWND wnd = GetDlgItem(mDlgHandle, ids[i]); + if (!wnd) { + return; + } + + // Get the button's dimensions in screen coordinates + RECT curRect; + if (!GetWindowRect(wnd, &curRect)) { + return; + } + + // Get (x,y) position of the button in client coordinates + POINT pt; + pt.x = curRect.left; + pt.y = curRect.top; + if (!ScreenToClient(mDlgHandle, &pt)) { + return; + } + + // Request the button's text margins + RECT margins; + if (!Button_GetTextMargin(wnd, &margins)) { + return; + } + + // Compute the button's width and height + SIZE curSize; + curSize.cx = curRect.right - curRect.left; + curSize.cy = curRect.bottom - curRect.top; + + // Request the button's ideal width and height and add in the margins + SIZE size = {0}; + if (!Button_GetIdealSize(wnd, &size)) { + return; + } + size.cx += margins.left + margins.right; + size.cy += margins.top + margins.bottom; + + // Size all buttons to be the same width as the longest button encountered + idealSize.cx = std::max(idealSize.cx, size.cx); + idealSize.cy = std::max(idealSize.cy, size.cy); + + // We won't bother resizing unless we need extra space + if (idealSize.cx > curSize.cx) { + needResizing = true; + } + + // Save the relevant info for the resize, if any. We do this even if + // needResizing is false because another button may trigger a resize later. + winInfo.push_back(WinInfo(wnd, pt, curSize)); + } + + if (!needResizing) { + return; + } + + // Pass 2: Resize the windows + int deltaX = 0; + HDWP hwp = BeginDeferWindowPos((int) winInfo.size()); + if (!hwp) { + return; + } + for (WinInfoVec::const_iterator itr = winInfo.begin(); + itr != winInfo.end(); ++itr) { + // deltaX accumulates the size changes so that each button's x coordinate + // can compensate for the width increases + deltaX += idealSize.cx - itr->size.cx; + hwp = DeferWindowPos(hwp, itr->hwnd, nullptr, + itr->pos.x - deltaX, itr->pos.y, + idealSize.cx, itr->size.cy, + SWP_NOZORDER | SWP_NOACTIVATE); + if (!hwp) { + return; + } + } + EndDeferWindowPos(hwp); +} + +INT_PTR +PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam, + LPARAM aLParam) +{ + mDlgHandle = aDlgHandle; + switch (aMsgCode) { + case WM_INITDIALOG: { + // Register a wait on the Firefox process so that we will be informed + // if it dies while the dialog is showing + RegisterWaitForSingleObject(&mRegWaitProcess, + mParentProcess, + &SOnParentProcessExit, + this, + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE); + SetWindowText(aDlgHandle, mWindowTitle); + SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText); + SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText); + SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText); + SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText); + ResizeButtons(); + HANDLE icon = LoadImage(nullptr, IDI_QUESTION, IMAGE_ICON, 0, 0, + LR_DEFAULTSIZE | LR_SHARED); + if (icon) { + SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0); + } + EnableWindow(mParentWindow, FALSE); + return TRUE; + } + case WM_CLOSE: { + mResponseBits |= HANGUI_USER_RESPONSE_CANCEL; + EndDialog(aDlgHandle, 0); + SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0); + return TRUE; + } + case WM_COMMAND: { + switch (LOWORD(aWParam)) { + case IDC_CONTINUE: + if (HIWORD(aWParam) == BN_CLICKED) { + mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE; + EndDialog(aDlgHandle, 1); + SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0); + return TRUE; + } + break; + case IDC_STOP: + if (HIWORD(aWParam) == BN_CLICKED) { + mResponseBits |= HANGUI_USER_RESPONSE_STOP; + EndDialog(aDlgHandle, 1); + SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0); + return TRUE; + } + break; + case IDC_NOFUTURE: + if (HIWORD(aWParam) == BN_CLICKED) { + if (Button_GetCheck(GetDlgItem(aDlgHandle, + IDC_NOFUTURE)) == BST_CHECKED) { + mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN; + } else { + mResponseBits &= + ~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN); + } + SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0); + return TRUE; + } + break; + default: + break; + } + break; + } + case WM_DESTROY: { + EnableWindow(mParentWindow, TRUE); + SetForegroundWindow(mParentWindow); + break; + } + default: + break; + } + return FALSE; +} + +// static +VOID CALLBACK +PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer) +{ + // Simulate a cancel if the parent process died + PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject); + object->RecvCancel(); +} + +bool +PluginHangUIChild::RecvShow() +{ + return (QueueUserAPC(&ShowAPC, + mMainThread, + reinterpret_cast<ULONG_PTR>(this))); +} + +bool +PluginHangUIChild::Show() +{ + INT_PTR dlgResult = DialogBox(GetModuleHandle(nullptr), + MAKEINTRESOURCE(IDD_HANGUIDLG), + nullptr, + &SHangUIDlgProc); + mDlgHandle = nullptr; + assert(dlgResult != -1); + bool result = false; + if (dlgResult != -1) { + PluginHangUIResponse* response = nullptr; + nsresult rv = mMiniShm.GetWritePtr(response); + if (NS_SUCCEEDED(rv)) { + response->mResponseBits = mResponseBits; + result = NS_SUCCEEDED(mMiniShm.Send()); + } + } + return result; +} + +// static +VOID CALLBACK +PluginHangUIChild::ShowAPC(ULONG_PTR aContext) +{ + PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext); + object->Show(); +} + +bool +PluginHangUIChild::RecvCancel() +{ + if (mDlgHandle) { + PostMessage(mDlgHandle, WM_CLOSE, 0, 0); + } + return true; +} + +bool +PluginHangUIChild::WaitForDismissal() +{ + if (!SetMainThread()) { + return false; + } + DWORD waitResult = WaitForSingleObjectEx(mParentProcess, + mIPCTimeoutMs, + TRUE); + return waitResult == WAIT_OBJECT_0 || + waitResult == WAIT_IO_COMPLETION; +} + +bool +PluginHangUIChild::SetMainThread() +{ + if (mMainThread) { + CloseHandle(mMainThread); + mMainThread = nullptr; + } + mMainThread = OpenThread(THREAD_SET_CONTEXT, + FALSE, + GetCurrentThreadId()); + return !(!mMainThread); +} + +} // namespace plugins +} // namespace mozilla + +#ifdef __MINGW32__ +extern "C" +#endif +int +wmain(int argc, wchar_t *argv[]) +{ + INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX), + ICC_STANDARD_CLASSES }; + if (!InitCommonControlsEx(&icc)) { + return 1; + } + mozilla::plugins::PluginHangUIChild hangui; + if (!hangui.Init(argc, argv)) { + return 1; + } + if (!hangui.WaitForDismissal()) { + return 1; + } + return 0; +} + |