dom/plugins/ipc/hangui/PluginHangUIChild.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:5d4e89f9e070
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/. */
6
7 #include "PluginHangUI.h"
8
9 #include "PluginHangUIChild.h"
10 #include "HangUIDlg.h"
11
12 #include <assert.h>
13 #include <commctrl.h>
14 #include <windowsx.h>
15 #include <algorithm>
16 #include <sstream>
17 #include <vector>
18
19 namespace mozilla {
20 namespace plugins {
21
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;
37
38 PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
39 const int PluginHangUIChild::kExpectedMinimumArgc = 10;
40
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 }
51
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 }
65
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 }
105
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 }
115
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 }
136
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 }
148
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]);
155
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 }
165
166 // Get the button's dimensions in screen coordinates
167 RECT curRect;
168 if (!GetWindowRect(wnd, &curRect)) {
169 return;
170 }
171
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 }
179
180 // Request the button's text margins
181 RECT margins;
182 if (!Button_GetTextMargin(wnd, &margins)) {
183 return;
184 }
185
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;
190
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;
198
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);
202
203 // We won't bother resizing unless we need extra space
204 if (idealSize.cx > curSize.cx) {
205 needResizing = true;
206 }
207
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 }
212
213 if (!needResizing) {
214 return;
215 }
216
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 }
238
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 }
320
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 }
329
330 bool
331 PluginHangUIChild::RecvShow()
332 {
333 return (QueueUserAPC(&ShowAPC,
334 mMainThread,
335 reinterpret_cast<ULONG_PTR>(this)));
336 }
337
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 }
358
359 // static
360 VOID CALLBACK
361 PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
362 {
363 PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
364 object->Show();
365 }
366
367 bool
368 PluginHangUIChild::RecvCancel()
369 {
370 if (mDlgHandle) {
371 PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
372 }
373 return true;
374 }
375
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 }
388
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 }
401
402 } // namespace plugins
403 } // namespace mozilla
404
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 }
425

mercurial