зеркало из https://github.com/mozilla/gecko-dev.git
426 строки
11 KiB
C++
426 строки
11 KiB
C++
/* -*- 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;
|
|
}
|
|
|