dom/plugins/ipc/hangui/PluginHangUIChild.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "PluginHangUI.h"
     9 #include "PluginHangUIChild.h"
    10 #include "HangUIDlg.h"
    12 #include <assert.h>
    13 #include <commctrl.h>
    14 #include <windowsx.h>
    15 #include <algorithm>
    16 #include <sstream>
    17 #include <vector>
    19 namespace mozilla {
    20 namespace plugins {
    22 struct WinInfo
    23 {
    24   WinInfo(HWND aHwnd, POINT& aPos, SIZE& aSize)
    25     :hwnd(aHwnd)
    26   {
    27     pos.x = aPos.x;
    28     pos.y = aPos.y;
    29     size.cx = aSize.cx;
    30     size.cy = aSize.cy;
    31   }
    32   HWND  hwnd;
    33   POINT pos;
    34   SIZE  size;
    35 };
    36 typedef std::vector<WinInfo> WinInfoVec;
    38 PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
    39 const int PluginHangUIChild::kExpectedMinimumArgc = 10;
    41 PluginHangUIChild::PluginHangUIChild()
    42   : mResponseBits(0),
    43     mParentWindow(nullptr),
    44     mDlgHandle(nullptr),
    45     mMainThread(nullptr),
    46     mParentProcess(nullptr),
    47     mRegWaitProcess(nullptr),
    48     mIPCTimeoutMs(0)
    49 {
    50 }
    52 PluginHangUIChild::~PluginHangUIChild()
    53 {
    54   if (mMainThread) {
    55     CloseHandle(mMainThread);
    56   }
    57   if (mRegWaitProcess) {
    58     UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE);
    59   }
    60   if (mParentProcess) {
    61     CloseHandle(mParentProcess);
    62   }
    63   sSelf = nullptr;
    64 }
    66 bool
    67 PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[])
    68 {
    69   if (aArgc < kExpectedMinimumArgc) {
    70     return false;
    71   }
    72   unsigned int i = 1;
    73   mMessageText = aArgv[i];
    74   mWindowTitle = aArgv[++i];
    75   mWaitBtnText = aArgv[++i];
    76   mKillBtnText = aArgv[++i];
    77   mNoFutureText = aArgv[++i];
    78   std::wistringstream issHwnd(aArgv[++i]);
    79   issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow);
    80   if (!issHwnd) {
    81     return false;
    82   }
    83   std::wistringstream issProc(aArgv[++i]);
    84   issProc >> mParentProcess;
    85   if (!issProc) {
    86     return false;
    87   }
    88   // Only set the App User Model ID if it's present in the args
    89   if (wcscmp(aArgv[++i], L"-")) {
    90     HMODULE shell32 = LoadLibrary(L"shell32.dll");
    91     if (shell32) {
    92       SETAPPUSERMODELID fSetAppUserModelID = (SETAPPUSERMODELID)
    93         GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID");
    94       if (fSetAppUserModelID) {
    95         fSetAppUserModelID(aArgv[i]);
    96       }
    97       FreeLibrary(shell32);
    98     }
    99   }
   100   std::wistringstream issTimeout(aArgv[++i]);
   101   issTimeout >> mIPCTimeoutMs;
   102   if (!issTimeout) {
   103     return false;
   104   }
   106   nsresult rv = mMiniShm.Init(this,
   107                               std::wstring(aArgv[++i]),
   108                               IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs);
   109   if (NS_FAILED(rv)) {
   110     return false;
   111   }
   112   sSelf = this;
   113   return true;
   114 }
   116 void
   117 PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj)
   118 {
   119   const PluginHangUICommand* cmd = nullptr;
   120   nsresult rv = aMiniShmObj->GetReadPtr(cmd);
   121   assert(NS_SUCCEEDED(rv));
   122   bool returnStatus = false;
   123   if (NS_SUCCEEDED(rv)) {
   124     switch (cmd->mCode) {
   125       case PluginHangUICommand::HANGUI_CMD_SHOW:
   126         returnStatus = RecvShow();
   127         break;
   128       case PluginHangUICommand::HANGUI_CMD_CANCEL:
   129         returnStatus = RecvCancel();
   130         break;
   131       default:
   132         break;
   133     }
   134   }
   135 }
   137 // static
   138 INT_PTR CALLBACK
   139 PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode,
   140                                   WPARAM aWParam, LPARAM aLParam)
   141 {
   142   PluginHangUIChild *self = PluginHangUIChild::sSelf;
   143   if (self) {
   144     return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam);
   145   }
   146   return FALSE;
   147 }
   149 void
   150 PluginHangUIChild::ResizeButtons()
   151 {
   152   // Control IDs are specified right-to-left as they appear in the dialog
   153   UINT ids[] = { IDC_STOP, IDC_CONTINUE };
   154   UINT numIds = sizeof(ids)/sizeof(ids[0]);
   156   // Pass 1: Compute the ideal size
   157   bool needResizing = false;
   158   SIZE idealSize = {0};
   159   WinInfoVec winInfo;
   160   for (UINT i = 0; i < numIds; ++i) {
   161     HWND wnd = GetDlgItem(mDlgHandle, ids[i]);
   162     if (!wnd) {
   163       return;
   164     }
   166     // Get the button's dimensions in screen coordinates
   167     RECT curRect;
   168     if (!GetWindowRect(wnd, &curRect)) {
   169       return;
   170     }
   172     // Get (x,y) position of the button in client coordinates
   173     POINT pt;
   174     pt.x = curRect.left;
   175     pt.y = curRect.top;
   176     if (!ScreenToClient(mDlgHandle, &pt)) {
   177       return;
   178     }
   180     // Request the button's text margins
   181     RECT margins;
   182     if (!Button_GetTextMargin(wnd, &margins)) {
   183       return;
   184     }
   186     // Compute the button's width and height
   187     SIZE curSize;
   188     curSize.cx = curRect.right - curRect.left;
   189     curSize.cy = curRect.bottom - curRect.top;
   191     // Request the button's ideal width and height and add in the margins
   192     SIZE size = {0};
   193     if (!Button_GetIdealSize(wnd, &size)) {
   194       return;
   195     }
   196     size.cx += margins.left + margins.right;
   197     size.cy += margins.top + margins.bottom;
   199     // Size all buttons to be the same width as the longest button encountered
   200     idealSize.cx = std::max(idealSize.cx, size.cx);
   201     idealSize.cy = std::max(idealSize.cy, size.cy);
   203     // We won't bother resizing unless we need extra space
   204     if (idealSize.cx > curSize.cx) {
   205       needResizing = true;
   206     }
   208     // Save the relevant info for the resize, if any. We do this even if 
   209     // needResizing is false because another button may trigger a resize later.
   210     winInfo.push_back(WinInfo(wnd, pt, curSize));
   211   }
   213   if (!needResizing) {
   214     return;
   215   }
   217   // Pass 2: Resize the windows
   218   int deltaX = 0;
   219   HDWP hwp = BeginDeferWindowPos((int) winInfo.size());
   220   if (!hwp) {
   221     return;
   222   }
   223   for (WinInfoVec::const_iterator itr = winInfo.begin();
   224        itr != winInfo.end(); ++itr) {
   225     // deltaX accumulates the size changes so that each button's x coordinate 
   226     // can compensate for the width increases
   227     deltaX += idealSize.cx - itr->size.cx;
   228     hwp = DeferWindowPos(hwp, itr->hwnd, nullptr,
   229                          itr->pos.x - deltaX, itr->pos.y,
   230                          idealSize.cx, itr->size.cy,
   231                          SWP_NOZORDER | SWP_NOACTIVATE);
   232     if (!hwp) {
   233       return;
   234     }
   235   }
   236   EndDeferWindowPos(hwp);
   237 }
   239 INT_PTR
   240 PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
   241                                  LPARAM aLParam)
   242 {
   243   mDlgHandle = aDlgHandle;
   244   switch (aMsgCode) {
   245     case WM_INITDIALOG: {
   246       // Register a wait on the Firefox process so that we will be informed
   247       // if it dies while the dialog is showing
   248       RegisterWaitForSingleObject(&mRegWaitProcess,
   249                                   mParentProcess,
   250                                   &SOnParentProcessExit,
   251                                   this,
   252                                   INFINITE,
   253                                   WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
   254       SetWindowText(aDlgHandle, mWindowTitle);
   255       SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText);
   256       SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText);
   257       SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText);
   258       SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText);
   259       ResizeButtons();
   260       HANDLE icon = LoadImage(nullptr, IDI_QUESTION, IMAGE_ICON, 0, 0,
   261                               LR_DEFAULTSIZE | LR_SHARED);
   262       if (icon) {
   263         SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0);
   264       }
   265       EnableWindow(mParentWindow, FALSE);
   266       return TRUE;
   267     }
   268     case WM_CLOSE: {
   269       mResponseBits |= HANGUI_USER_RESPONSE_CANCEL;
   270       EndDialog(aDlgHandle, 0);
   271       SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   272       return TRUE;
   273     }
   274     case WM_COMMAND: {
   275       switch (LOWORD(aWParam)) {
   276         case IDC_CONTINUE:
   277           if (HIWORD(aWParam) == BN_CLICKED) {
   278             mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE;
   279             EndDialog(aDlgHandle, 1);
   280             SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   281             return TRUE;
   282           }
   283           break;
   284         case IDC_STOP:
   285           if (HIWORD(aWParam) == BN_CLICKED) {
   286             mResponseBits |= HANGUI_USER_RESPONSE_STOP;
   287             EndDialog(aDlgHandle, 1);
   288             SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   289             return TRUE;
   290           }
   291           break;
   292         case IDC_NOFUTURE:
   293           if (HIWORD(aWParam) == BN_CLICKED) {
   294             if (Button_GetCheck(GetDlgItem(aDlgHandle,
   295                                            IDC_NOFUTURE)) == BST_CHECKED) {
   296               mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN;
   297             } else {
   298               mResponseBits &=
   299                 ~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
   300             }
   301             SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
   302             return TRUE;
   303           }
   304           break;
   305         default:
   306           break;
   307       }
   308       break;
   309     }
   310     case WM_DESTROY: {
   311       EnableWindow(mParentWindow, TRUE);
   312       SetForegroundWindow(mParentWindow);
   313       break;
   314     }
   315     default:
   316       break;
   317   }
   318   return FALSE;
   319 }
   321 // static
   322 VOID CALLBACK
   323 PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer)
   324 {
   325   // Simulate a cancel if the parent process died
   326   PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject);
   327   object->RecvCancel();
   328 }
   330 bool
   331 PluginHangUIChild::RecvShow()
   332 {
   333   return (QueueUserAPC(&ShowAPC,
   334                        mMainThread,
   335                        reinterpret_cast<ULONG_PTR>(this)));
   336 }
   338 bool
   339 PluginHangUIChild::Show()
   340 {
   341   INT_PTR dlgResult = DialogBox(GetModuleHandle(nullptr),
   342                                 MAKEINTRESOURCE(IDD_HANGUIDLG),
   343                                 nullptr,
   344                                 &SHangUIDlgProc);
   345   mDlgHandle = nullptr;
   346   assert(dlgResult != -1);
   347   bool result = false;
   348   if (dlgResult != -1) {
   349     PluginHangUIResponse* response = nullptr;
   350     nsresult rv = mMiniShm.GetWritePtr(response);
   351     if (NS_SUCCEEDED(rv)) {
   352       response->mResponseBits = mResponseBits;
   353       result = NS_SUCCEEDED(mMiniShm.Send());
   354     }
   355   }
   356   return result;
   357 }
   359 // static
   360 VOID CALLBACK
   361 PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
   362 {
   363   PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
   364   object->Show();
   365 }
   367 bool
   368 PluginHangUIChild::RecvCancel()
   369 {
   370   if (mDlgHandle) {
   371     PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
   372   }
   373   return true;
   374 }
   376 bool
   377 PluginHangUIChild::WaitForDismissal()
   378 {
   379   if (!SetMainThread()) {
   380     return false;
   381   }
   382   DWORD waitResult = WaitForSingleObjectEx(mParentProcess,
   383                                            mIPCTimeoutMs,
   384                                            TRUE);
   385   return waitResult == WAIT_OBJECT_0 ||
   386          waitResult == WAIT_IO_COMPLETION;
   387 }
   389 bool
   390 PluginHangUIChild::SetMainThread()
   391 {
   392   if (mMainThread) {
   393     CloseHandle(mMainThread);
   394     mMainThread = nullptr;
   395   }
   396   mMainThread = OpenThread(THREAD_SET_CONTEXT,
   397                            FALSE,
   398                            GetCurrentThreadId());
   399   return !(!mMainThread);
   400 }
   402 } // namespace plugins
   403 } // namespace mozilla
   405 #ifdef __MINGW32__
   406 extern "C"
   407 #endif
   408 int
   409 wmain(int argc, wchar_t *argv[])
   410 {
   411   INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX),
   412                                ICC_STANDARD_CLASSES };
   413   if (!InitCommonControlsEx(&icc)) {
   414     return 1;
   415   }
   416   mozilla::plugins::PluginHangUIChild hangui;
   417   if (!hangui.Init(argc, argv)) {
   418     return 1;
   419   }
   420   if (!hangui.WaitForDismissal()) {
   421     return 1;
   422   }
   423   return 0;
   424 }

mercurial