/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 #include #include #include #include #include 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 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(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(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(aObject); object->RecvCancel(); } bool PluginHangUIChild::RecvShow() { return (QueueUserAPC(&ShowAPC, mMainThread, reinterpret_cast(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(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; }