dom/plugins/ipc/hangui/PluginHangUIChild.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,425 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "PluginHangUI.h"
    1.11 +
    1.12 +#include "PluginHangUIChild.h"
    1.13 +#include "HangUIDlg.h"
    1.14 +
    1.15 +#include <assert.h>
    1.16 +#include <commctrl.h>
    1.17 +#include <windowsx.h>
    1.18 +#include <algorithm>
    1.19 +#include <sstream>
    1.20 +#include <vector>
    1.21 +
    1.22 +namespace mozilla {
    1.23 +namespace plugins {
    1.24 +
    1.25 +struct WinInfo
    1.26 +{
    1.27 +  WinInfo(HWND aHwnd, POINT& aPos, SIZE& aSize)
    1.28 +    :hwnd(aHwnd)
    1.29 +  {
    1.30 +    pos.x = aPos.x;
    1.31 +    pos.y = aPos.y;
    1.32 +    size.cx = aSize.cx;
    1.33 +    size.cy = aSize.cy;
    1.34 +  }
    1.35 +  HWND  hwnd;
    1.36 +  POINT pos;
    1.37 +  SIZE  size;
    1.38 +};
    1.39 +typedef std::vector<WinInfo> WinInfoVec;
    1.40 +
    1.41 +PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
    1.42 +const int PluginHangUIChild::kExpectedMinimumArgc = 10;
    1.43 +
    1.44 +PluginHangUIChild::PluginHangUIChild()
    1.45 +  : mResponseBits(0),
    1.46 +    mParentWindow(nullptr),
    1.47 +    mDlgHandle(nullptr),
    1.48 +    mMainThread(nullptr),
    1.49 +    mParentProcess(nullptr),
    1.50 +    mRegWaitProcess(nullptr),
    1.51 +    mIPCTimeoutMs(0)
    1.52 +{
    1.53 +}
    1.54 +
    1.55 +PluginHangUIChild::~PluginHangUIChild()
    1.56 +{
    1.57 +  if (mMainThread) {
    1.58 +    CloseHandle(mMainThread);
    1.59 +  }
    1.60 +  if (mRegWaitProcess) {
    1.61 +    UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE);
    1.62 +  }
    1.63 +  if (mParentProcess) {
    1.64 +    CloseHandle(mParentProcess);
    1.65 +  }
    1.66 +  sSelf = nullptr;
    1.67 +}
    1.68 +
    1.69 +bool
    1.70 +PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[])
    1.71 +{
    1.72 +  if (aArgc < kExpectedMinimumArgc) {
    1.73 +    return false;
    1.74 +  }
    1.75 +  unsigned int i = 1;
    1.76 +  mMessageText = aArgv[i];
    1.77 +  mWindowTitle = aArgv[++i];
    1.78 +  mWaitBtnText = aArgv[++i];
    1.79 +  mKillBtnText = aArgv[++i];
    1.80 +  mNoFutureText = aArgv[++i];
    1.81 +  std::wistringstream issHwnd(aArgv[++i]);
    1.82 +  issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow);
    1.83 +  if (!issHwnd) {
    1.84 +    return false;
    1.85 +  }
    1.86 +  std::wistringstream issProc(aArgv[++i]);
    1.87 +  issProc >> mParentProcess;
    1.88 +  if (!issProc) {
    1.89 +    return false;
    1.90 +  }
    1.91 +  // Only set the App User Model ID if it's present in the args
    1.92 +  if (wcscmp(aArgv[++i], L"-")) {
    1.93 +    HMODULE shell32 = LoadLibrary(L"shell32.dll");
    1.94 +    if (shell32) {
    1.95 +      SETAPPUSERMODELID fSetAppUserModelID = (SETAPPUSERMODELID)
    1.96 +        GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID");
    1.97 +      if (fSetAppUserModelID) {
    1.98 +        fSetAppUserModelID(aArgv[i]);
    1.99 +      }
   1.100 +      FreeLibrary(shell32);
   1.101 +    }
   1.102 +  }
   1.103 +  std::wistringstream issTimeout(aArgv[++i]);
   1.104 +  issTimeout >> mIPCTimeoutMs;
   1.105 +  if (!issTimeout) {
   1.106 +    return false;
   1.107 +  }
   1.108 +
   1.109 +  nsresult rv = mMiniShm.Init(this,
   1.110 +                              std::wstring(aArgv[++i]),
   1.111 +                              IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs);
   1.112 +  if (NS_FAILED(rv)) {
   1.113 +    return false;
   1.114 +  }
   1.115 +  sSelf = this;
   1.116 +  return true;
   1.117 +}
   1.118 +
   1.119 +void
   1.120 +PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj)
   1.121 +{
   1.122 +  const PluginHangUICommand* cmd = nullptr;
   1.123 +  nsresult rv = aMiniShmObj->GetReadPtr(cmd);
   1.124 +  assert(NS_SUCCEEDED(rv));
   1.125 +  bool returnStatus = false;
   1.126 +  if (NS_SUCCEEDED(rv)) {
   1.127 +    switch (cmd->mCode) {
   1.128 +      case PluginHangUICommand::HANGUI_CMD_SHOW:
   1.129 +        returnStatus = RecvShow();
   1.130 +        break;
   1.131 +      case PluginHangUICommand::HANGUI_CMD_CANCEL:
   1.132 +        returnStatus = RecvCancel();
   1.133 +        break;
   1.134 +      default:
   1.135 +        break;
   1.136 +    }
   1.137 +  }
   1.138 +}
   1.139 +
   1.140 +// static
   1.141 +INT_PTR CALLBACK
   1.142 +PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode,
   1.143 +                                  WPARAM aWParam, LPARAM aLParam)
   1.144 +{
   1.145 +  PluginHangUIChild *self = PluginHangUIChild::sSelf;
   1.146 +  if (self) {
   1.147 +    return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam);
   1.148 +  }
   1.149 +  return FALSE;
   1.150 +}
   1.151 +
   1.152 +void
   1.153 +PluginHangUIChild::ResizeButtons()
   1.154 +{
   1.155 +  // Control IDs are specified right-to-left as they appear in the dialog
   1.156 +  UINT ids[] = { IDC_STOP, IDC_CONTINUE };
   1.157 +  UINT numIds = sizeof(ids)/sizeof(ids[0]);
   1.158 +
   1.159 +  // Pass 1: Compute the ideal size
   1.160 +  bool needResizing = false;
   1.161 +  SIZE idealSize = {0};
   1.162 +  WinInfoVec winInfo;
   1.163 +  for (UINT i = 0; i < numIds; ++i) {
   1.164 +    HWND wnd = GetDlgItem(mDlgHandle, ids[i]);
   1.165 +    if (!wnd) {
   1.166 +      return;
   1.167 +    }
   1.168 +
   1.169 +    // Get the button's dimensions in screen coordinates
   1.170 +    RECT curRect;
   1.171 +    if (!GetWindowRect(wnd, &curRect)) {
   1.172 +      return;
   1.173 +    }
   1.174 +
   1.175 +    // Get (x,y) position of the button in client coordinates
   1.176 +    POINT pt;
   1.177 +    pt.x = curRect.left;
   1.178 +    pt.y = curRect.top;
   1.179 +    if (!ScreenToClient(mDlgHandle, &pt)) {
   1.180 +      return;
   1.181 +    }
   1.182 +
   1.183 +    // Request the button's text margins
   1.184 +    RECT margins;
   1.185 +    if (!Button_GetTextMargin(wnd, &margins)) {
   1.186 +      return;
   1.187 +    }
   1.188 +
   1.189 +    // Compute the button's width and height
   1.190 +    SIZE curSize;
   1.191 +    curSize.cx = curRect.right - curRect.left;
   1.192 +    curSize.cy = curRect.bottom - curRect.top;
   1.193 +
   1.194 +    // Request the button's ideal width and height and add in the margins
   1.195 +    SIZE size = {0};
   1.196 +    if (!Button_GetIdealSize(wnd, &size)) {
   1.197 +      return;
   1.198 +    }
   1.199 +    size.cx += margins.left + margins.right;
   1.200 +    size.cy += margins.top + margins.bottom;
   1.201 +
   1.202 +    // Size all buttons to be the same width as the longest button encountered
   1.203 +    idealSize.cx = std::max(idealSize.cx, size.cx);
   1.204 +    idealSize.cy = std::max(idealSize.cy, size.cy);
   1.205 +
   1.206 +    // We won't bother resizing unless we need extra space
   1.207 +    if (idealSize.cx > curSize.cx) {
   1.208 +      needResizing = true;
   1.209 +    }
   1.210 +
   1.211 +    // Save the relevant info for the resize, if any. We do this even if 
   1.212 +    // needResizing is false because another button may trigger a resize later.
   1.213 +    winInfo.push_back(WinInfo(wnd, pt, curSize));
   1.214 +  }
   1.215 +
   1.216 +  if (!needResizing) {
   1.217 +    return;
   1.218 +  }
   1.219 +
   1.220 +  // Pass 2: Resize the windows
   1.221 +  int deltaX = 0;
   1.222 +  HDWP hwp = BeginDeferWindowPos((int) winInfo.size());
   1.223 +  if (!hwp) {
   1.224 +    return;
   1.225 +  }
   1.226 +  for (WinInfoVec::const_iterator itr = winInfo.begin();
   1.227 +       itr != winInfo.end(); ++itr) {
   1.228 +    // deltaX accumulates the size changes so that each button's x coordinate 
   1.229 +    // can compensate for the width increases
   1.230 +    deltaX += idealSize.cx - itr->size.cx;
   1.231 +    hwp = DeferWindowPos(hwp, itr->hwnd, nullptr,
   1.232 +                         itr->pos.x - deltaX, itr->pos.y,
   1.233 +                         idealSize.cx, itr->size.cy,
   1.234 +                         SWP_NOZORDER | SWP_NOACTIVATE);
   1.235 +    if (!hwp) {
   1.236 +      return;
   1.237 +    }
   1.238 +  }
   1.239 +  EndDeferWindowPos(hwp);
   1.240 +}
   1.241 +
   1.242 +INT_PTR
   1.243 +PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
   1.244 +                                 LPARAM aLParam)
   1.245 +{
   1.246 +  mDlgHandle = aDlgHandle;
   1.247 +  switch (aMsgCode) {
   1.248 +    case WM_INITDIALOG: {
   1.249 +      // Register a wait on the Firefox process so that we will be informed
   1.250 +      // if it dies while the dialog is showing
   1.251 +      RegisterWaitForSingleObject(&mRegWaitProcess,
   1.252 +                                  mParentProcess,
   1.253 +                                  &SOnParentProcessExit,
   1.254 +                                  this,
   1.255 +                                  INFINITE,
   1.256 +                                  WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
   1.257 +      SetWindowText(aDlgHandle, mWindowTitle);
   1.258 +      SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText);
   1.259 +      SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText);
   1.260 +      SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText);
   1.261 +      SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText);
   1.262 +      ResizeButtons();
   1.263 +      HANDLE icon = LoadImage(nullptr, IDI_QUESTION, IMAGE_ICON, 0, 0,
   1.264 +                              LR_DEFAULTSIZE | LR_SHARED);
   1.265 +      if (icon) {
   1.266 +        SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0);
   1.267 +      }
   1.268 +      EnableWindow(mParentWindow, FALSE);
   1.269 +      return TRUE;
   1.270 +    }
   1.271 +    case WM_CLOSE: {
   1.272 +      mResponseBits |= HANGUI_USER_RESPONSE_CANCEL;
   1.273 +      EndDialog(aDlgHandle, 0);
   1.274 +      SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   1.275 +      return TRUE;
   1.276 +    }
   1.277 +    case WM_COMMAND: {
   1.278 +      switch (LOWORD(aWParam)) {
   1.279 +        case IDC_CONTINUE:
   1.280 +          if (HIWORD(aWParam) == BN_CLICKED) {
   1.281 +            mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE;
   1.282 +            EndDialog(aDlgHandle, 1);
   1.283 +            SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   1.284 +            return TRUE;
   1.285 +          }
   1.286 +          break;
   1.287 +        case IDC_STOP:
   1.288 +          if (HIWORD(aWParam) == BN_CLICKED) {
   1.289 +            mResponseBits |= HANGUI_USER_RESPONSE_STOP;
   1.290 +            EndDialog(aDlgHandle, 1);
   1.291 +            SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   1.292 +            return TRUE;
   1.293 +          }
   1.294 +          break;
   1.295 +        case IDC_NOFUTURE:
   1.296 +          if (HIWORD(aWParam) == BN_CLICKED) {
   1.297 +            if (Button_GetCheck(GetDlgItem(aDlgHandle,
   1.298 +                                           IDC_NOFUTURE)) == BST_CHECKED) {
   1.299 +              mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN;
   1.300 +            } else {
   1.301 +              mResponseBits &=
   1.302 +                ~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
   1.303 +            }
   1.304 +            SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   1.305 +            return TRUE;
   1.306 +          }
   1.307 +          break;
   1.308 +        default:
   1.309 +          break;
   1.310 +      }
   1.311 +      break;
   1.312 +    }
   1.313 +    case WM_DESTROY: {
   1.314 +      EnableWindow(mParentWindow, TRUE);
   1.315 +      SetForegroundWindow(mParentWindow);
   1.316 +      break;
   1.317 +    }
   1.318 +    default:
   1.319 +      break;
   1.320 +  }
   1.321 +  return FALSE;
   1.322 +}
   1.323 +
   1.324 +// static
   1.325 +VOID CALLBACK
   1.326 +PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer)
   1.327 +{
   1.328 +  // Simulate a cancel if the parent process died
   1.329 +  PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject);
   1.330 +  object->RecvCancel();
   1.331 +}
   1.332 +
   1.333 +bool
   1.334 +PluginHangUIChild::RecvShow()
   1.335 +{
   1.336 +  return (QueueUserAPC(&ShowAPC,
   1.337 +                       mMainThread,
   1.338 +                       reinterpret_cast<ULONG_PTR>(this)));
   1.339 +}
   1.340 +
   1.341 +bool
   1.342 +PluginHangUIChild::Show()
   1.343 +{
   1.344 +  INT_PTR dlgResult = DialogBox(GetModuleHandle(nullptr),
   1.345 +                                MAKEINTRESOURCE(IDD_HANGUIDLG),
   1.346 +                                nullptr,
   1.347 +                                &SHangUIDlgProc);
   1.348 +  mDlgHandle = nullptr;
   1.349 +  assert(dlgResult != -1);
   1.350 +  bool result = false;
   1.351 +  if (dlgResult != -1) {
   1.352 +    PluginHangUIResponse* response = nullptr;
   1.353 +    nsresult rv = mMiniShm.GetWritePtr(response);
   1.354 +    if (NS_SUCCEEDED(rv)) {
   1.355 +      response->mResponseBits = mResponseBits;
   1.356 +      result = NS_SUCCEEDED(mMiniShm.Send());
   1.357 +    }
   1.358 +  }
   1.359 +  return result;
   1.360 +}
   1.361 +
   1.362 +// static
   1.363 +VOID CALLBACK
   1.364 +PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
   1.365 +{
   1.366 +  PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
   1.367 +  object->Show();
   1.368 +}
   1.369 +
   1.370 +bool
   1.371 +PluginHangUIChild::RecvCancel()
   1.372 +{
   1.373 +  if (mDlgHandle) {
   1.374 +    PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
   1.375 +  }
   1.376 +  return true;
   1.377 +}
   1.378 +
   1.379 +bool
   1.380 +PluginHangUIChild::WaitForDismissal()
   1.381 +{
   1.382 +  if (!SetMainThread()) {
   1.383 +    return false;
   1.384 +  }
   1.385 +  DWORD waitResult = WaitForSingleObjectEx(mParentProcess,
   1.386 +                                           mIPCTimeoutMs,
   1.387 +                                           TRUE);
   1.388 +  return waitResult == WAIT_OBJECT_0 ||
   1.389 +         waitResult == WAIT_IO_COMPLETION;
   1.390 +}
   1.391 +
   1.392 +bool
   1.393 +PluginHangUIChild::SetMainThread()
   1.394 +{
   1.395 +  if (mMainThread) {
   1.396 +    CloseHandle(mMainThread);
   1.397 +    mMainThread = nullptr;
   1.398 +  }
   1.399 +  mMainThread = OpenThread(THREAD_SET_CONTEXT,
   1.400 +                           FALSE,
   1.401 +                           GetCurrentThreadId());
   1.402 +  return !(!mMainThread);
   1.403 +}
   1.404 +
   1.405 +} // namespace plugins
   1.406 +} // namespace mozilla
   1.407 +
   1.408 +#ifdef __MINGW32__
   1.409 +extern "C"
   1.410 +#endif
   1.411 +int
   1.412 +wmain(int argc, wchar_t *argv[])
   1.413 +{
   1.414 +  INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX),
   1.415 +                               ICC_STANDARD_CLASSES };
   1.416 +  if (!InitCommonControlsEx(&icc)) {
   1.417 +    return 1;
   1.418 +  }
   1.419 +  mozilla::plugins::PluginHangUIChild hangui;
   1.420 +  if (!hangui.Init(argc, argv)) {
   1.421 +    return 1;
   1.422 +  }
   1.423 +  if (!hangui.WaitForDismissal()) {
   1.424 +    return 1;
   1.425 +  }
   1.426 +  return 0;
   1.427 +}
   1.428 +

mercurial