dom/plugins/ipc/hangui/PluginHangUIChild.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial