Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / ipc / glue / WindowsMessageLoop.cpp
blobab7f4b0a57e53f05d59c767966eea7d0cb0b828c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/DebugOnly.h"
10 #include "WindowsMessageLoop.h"
11 #include "MessageChannel.h"
13 #include "nsAutoPtr.h"
14 #include "nsServiceManagerUtils.h"
15 #include "nsString.h"
16 #include "nsIXULAppInfo.h"
17 #include "WinUtils.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/PaintTracker.h"
22 using namespace mozilla;
23 using namespace mozilla::ipc;
24 using namespace mozilla::ipc::windows;
26 /**
27 * The Windows-only code below exists to solve a general problem with deadlocks
28 * that we experience when sending synchronous IPC messages to processes that
29 * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous
30 * messages between parent and child HWNDs in multiple circumstances (e.g.
31 * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled
32 * by different threads or different processes. Thus we can very easily end up
33 * in a deadlock by a call stack like the following:
35 * Process A:
36 * - CreateWindow(...) creates a "parent" HWND.
37 * - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent"
38 * HWND over to Process B. Process A blocks until a response is received
39 * from Process B.
41 * Process B:
42 * - RecvCreateWidget(HWND) gets the "parent" HWND from Process A.
43 * - CreateWindow(..., HWND) creates a "child" HWND with the parent from
44 * process A.
45 * - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent
46 * synchronously to Process A. Process B blocks until a response is
47 * received from Process A. Process A, however, is blocked and cannot
48 * process the message. Both processes are deadlocked.
50 * The example above has a few different workarounds (e.g. setting the
51 * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is
52 * persists. Once two HWNDs are parented we must not block their owning
53 * threads when manipulating either HWND.
55 * Windows requires any application that hosts native HWNDs to always process
56 * messages or risk deadlock. Given our architecture the only way to meet
57 * Windows' requirement and allow for synchronous IPC messages is to pump a
58 * miniature message loop during a sync IPC call. We avoid processing any
59 * queued messages during the loop (with one exception, see below), but
60 * "nonqueued" messages (see
61 * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the
62 * section "Nonqueued messages") cannot be avoided. Those messages are trapped
63 * in a special window procedure where we can either ignore the message or
64 * process it in some fashion.
66 * Queued and "non-queued" messages will be processed during Interrupt calls if
67 * modal UI related api calls block an Interrupt in-call in the child. To prevent
68 * windows from freezing, and to allow concurrent processing of critical
69 * events (such as painting), we spin a native event dispatch loop while
70 * these in-calls are blocked.
73 #if defined(ACCESSIBILITY)
74 // pulled from accessibility's win utils
75 extern const wchar_t* kPropNameTabContent;
76 #endif
78 // widget related message id constants we need to defer
79 namespace mozilla {
80 namespace widget {
81 extern UINT sAppShellGoannaMsgId;
85 namespace {
87 const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
88 const wchar_t k3rdPartyWindowProp[] = L"Mozilla3rdPartyWindow";
90 // This isn't defined before Windows XP.
91 enum { WM_XP_THEMECHANGED = 0x031A };
93 char16_t gAppMessageWindowName[256] = { 0 };
94 int32_t gAppMessageWindowNameLength = 0;
96 nsTArray<HWND>* gNeuteredWindows = nullptr;
98 typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
99 DeferredMessageArray* gDeferredMessages = nullptr;
101 HHOOK gDeferredGetMsgHook = nullptr;
102 HHOOK gDeferredCallWndProcHook = nullptr;
104 DWORD gUIThreadId = 0;
105 HWND gCOMWindow = 0;
106 // Once initialized, gWinEventHook is never unhooked. We save the handle so
107 // that we can check whether or not the hook is initialized.
108 HWINEVENTHOOK gWinEventHook = nullptr;
109 const wchar_t kCOMWindowClassName[] = L"OleMainThreadWndClass";
111 // WM_GETOBJECT id pulled from uia headers
112 #define MOZOBJID_UIAROOT -25
114 HWND
115 FindCOMWindow()
117 MOZ_ASSERT(gUIThreadId);
119 HWND last = 0;
120 while ((last = FindWindowExW(HWND_MESSAGE, last, kCOMWindowClassName, NULL))) {
121 if (GetWindowThreadProcessId(last, NULL) == gUIThreadId) {
122 return last;
126 return (HWND)0;
129 void CALLBACK
130 WinEventHook(HWINEVENTHOOK aWinEventHook, DWORD aEvent, HWND aHwnd,
131 LONG aIdObject, LONG aIdChild, DWORD aEventThread,
132 DWORD aMsEventTime)
134 MOZ_ASSERT(aWinEventHook == gWinEventHook);
135 MOZ_ASSERT(gUIThreadId == aEventThread);
136 switch (aEvent) {
137 case EVENT_OBJECT_CREATE: {
138 if (aIdObject != OBJID_WINDOW || aIdChild != CHILDID_SELF) {
139 // Not an event we're interested in
140 return;
142 wchar_t classBuf[256] = {0};
143 int result = ::GetClassNameW(aHwnd, classBuf,
144 MOZ_ARRAY_LENGTH(classBuf));
145 if (result != (MOZ_ARRAY_LENGTH(kCOMWindowClassName) - 1) ||
146 wcsncmp(kCOMWindowClassName, classBuf, result)) {
147 // Not a class we're interested in
148 return;
150 MOZ_ASSERT(FindCOMWindow() == aHwnd);
151 gCOMWindow = aHwnd;
152 break;
154 case EVENT_OBJECT_DESTROY: {
155 if (aHwnd == gCOMWindow && aIdObject == OBJID_WINDOW) {
156 MOZ_ASSERT(aIdChild == CHILDID_SELF);
157 gCOMWindow = 0;
159 break;
161 default: {
162 return;
167 LRESULT CALLBACK
168 DeferredMessageHook(int nCode,
169 WPARAM wParam,
170 LPARAM lParam)
172 // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
173 // WH_GETMESSAGE hook, but they have different parameters. We don't
174 // use any of them except nCode which has the same meaning.
176 // Only run deferred messages if all of these conditions are met:
177 // 1. The |nCode| indicates that this hook should do something.
178 // 2. We have deferred messages to run.
179 // 3. We're not being called from the PeekMessage within the WaitFor*Notify
180 // function (indicated with MessageChannel::IsPumpingMessages). We really
181 // only want to run after returning to the main event loop.
182 if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) {
183 NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
184 "These hooks must be set if we're being called!");
185 NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
187 // Unset hooks first, in case we reenter below.
188 UnhookWindowsHookEx(gDeferredGetMsgHook);
189 UnhookWindowsHookEx(gDeferredCallWndProcHook);
190 gDeferredGetMsgHook = 0;
191 gDeferredCallWndProcHook = 0;
193 // Unset the global and make sure we delete it when we're done here.
194 nsAutoPtr<DeferredMessageArray> messages(gDeferredMessages);
195 gDeferredMessages = nullptr;
197 // Run all the deferred messages in order.
198 uint32_t count = messages->Length();
199 for (uint32_t index = 0; index < count; index++) {
200 messages->ElementAt(index)->Run();
204 // Always call the next hook.
205 return CallNextHookEx(nullptr, nCode, wParam, lParam);
208 void
209 ScheduleDeferredMessageRun()
211 if (gDeferredMessages &&
212 !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) {
213 NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
215 gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook,
216 nullptr, gUIThreadId);
217 gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,
218 DeferredMessageHook, nullptr,
219 gUIThreadId);
220 NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
221 "Failed to set hooks!");
225 static void
226 DumpNeuteredMessage(HWND hwnd, UINT uMsg)
228 #ifdef DEBUG
229 nsAutoCString log("Received \"nonqueued\" ");
230 // classify messages
231 if (uMsg < WM_USER) {
232 int idx = 0;
233 while (mozilla::widget::gAllEvents[idx].mId != (long)uMsg &&
234 mozilla::widget::gAllEvents[idx].mStr != nullptr) {
235 idx++;
237 if (mozilla::widget::gAllEvents[idx].mStr) {
238 log.AppendPrintf("ui message \"%s\"", mozilla::widget::gAllEvents[idx].mStr);
239 } else {
240 log.AppendPrintf("ui message (0x%X)", uMsg);
242 } else if (uMsg >= WM_USER && uMsg < WM_APP) {
243 log.AppendPrintf("WM_USER message (0x%X)", uMsg);
244 } else if (uMsg >= WM_APP && uMsg < 0xC000) {
245 log.AppendPrintf("WM_APP message (0x%X)", uMsg);
246 } else if (uMsg >= 0xC000 && uMsg < 0x10000) {
247 log.AppendPrintf("registered windows message (0x%X)", uMsg);
248 } else {
249 log.AppendPrintf("system message (0x%X)", uMsg);
252 log.AppendLiteral(" during a synchronous IPC message for window ");
253 log.AppendPrintf("0x%X", hwnd);
255 wchar_t className[256] = { 0 };
256 if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
257 log.AppendLiteral(" (\"");
258 log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
259 log.AppendLiteral("\")");
262 log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
263 "window procedure.");
264 NS_ERROR(log.get());
265 #endif
268 LRESULT
269 ProcessOrDeferMessage(HWND hwnd,
270 UINT uMsg,
271 WPARAM wParam,
272 LPARAM lParam)
274 DeferredMessage* deferred = nullptr;
276 // Most messages ask for 0 to be returned if the message is processed.
277 LRESULT res = 0;
279 switch (uMsg) {
280 // Messages that can be deferred as-is. These must not contain pointers in
281 // their wParam or lParam arguments!
282 case WM_ACTIVATE:
283 case WM_ACTIVATEAPP:
284 case WM_CANCELMODE:
285 case WM_CAPTURECHANGED:
286 case WM_CHILDACTIVATE:
287 case WM_DESTROY:
288 case WM_ENABLE:
289 case WM_IME_NOTIFY:
290 case WM_IME_SETCONTEXT:
291 case WM_KILLFOCUS:
292 case WM_MOUSEWHEEL:
293 case WM_NCDESTROY:
294 case WM_PARENTNOTIFY:
295 case WM_SETFOCUS:
296 case WM_SYSCOMMAND:
297 case WM_DISPLAYCHANGE:
298 case WM_SHOWWINDOW: // Intentional fall-through.
299 case WM_XP_THEMECHANGED: {
300 deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
301 break;
304 case WM_DEVICECHANGE:
305 case WM_POWERBROADCAST:
306 case WM_NCACTIVATE: // Intentional fall-through.
307 case WM_SETCURSOR: {
308 // Friggin unconventional return value...
309 res = TRUE;
310 deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
311 break;
314 case WM_MOUSEACTIVATE: {
315 res = MA_NOACTIVATE;
316 deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
317 break;
320 // These messages need to use the RedrawWindow function to generate the
321 // right kind of message. We can't simply fake them as the MSDN docs say
322 // explicitly that paint messages should not be sent by an application.
323 case WM_ERASEBKGND: {
324 UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT |
325 RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW;
326 deferred = new DeferredRedrawMessage(hwnd, flags);
327 break;
330 // This message will generate a WM_PAINT message if there are invalid
331 // areas.
332 case WM_PAINT: {
333 deferred = new DeferredUpdateMessage(hwnd);
334 break;
337 // This message holds a string in its lParam that we must copy.
338 case WM_SETTINGCHANGE: {
339 deferred = new DeferredSettingChangeMessage(hwnd, uMsg, wParam, lParam);
340 break;
343 // These messages are faked via a call to SetWindowPos.
344 case WM_WINDOWPOSCHANGED: {
345 deferred = new DeferredWindowPosMessage(hwnd, lParam);
346 break;
348 case WM_NCCALCSIZE: {
349 deferred = new DeferredWindowPosMessage(hwnd, lParam, true, wParam);
350 break;
353 case WM_COPYDATA: {
354 deferred = new DeferredCopyDataMessage(hwnd, uMsg, wParam, lParam);
355 res = TRUE;
356 break;
359 case WM_STYLECHANGED: {
360 deferred = new DeferredStyleChangeMessage(hwnd, wParam, lParam);
361 break;
364 case WM_SETICON: {
365 deferred = new DeferredSetIconMessage(hwnd, uMsg, wParam, lParam);
366 break;
369 // Messages that are safe to pass to DefWindowProc go here.
370 case WM_ENTERIDLE:
371 case WM_GETICON:
372 case WM_NCPAINT: // (never trap nc paint events)
373 case WM_GETMINMAXINFO:
374 case WM_GETTEXT:
375 case WM_NCHITTEST:
376 case WM_STYLECHANGING: // Intentional fall-through.
377 case WM_WINDOWPOSCHANGING:
378 case WM_GETTEXTLENGTH: {
379 return DefWindowProc(hwnd, uMsg, wParam, lParam);
382 // Just return, prevents DefWindowProc from messaging the window
383 // syncronously with other events, which may be deferred. Prevents
384 // random shutdown of aero composition on the window.
385 case WM_SYNCPAINT:
386 return 0;
388 // This message causes QuickTime to make re-entrant calls.
389 // Simply discarding it doesn't seem to hurt anything.
390 case WM_APP-1:
391 return 0;
393 // We only support a query for our IAccessible or UIA pointers.
394 // This should be safe, and needs to be sync.
395 #if defined(ACCESSIBILITY)
396 case WM_GETOBJECT: {
397 if (!::GetPropW(hwnd, k3rdPartyWindowProp)) {
398 DWORD objId = static_cast<DWORD>(lParam);
399 if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT)) {
400 WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
401 if (oldWndProc) {
402 return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
406 return DefWindowProc(hwnd, uMsg, wParam, lParam);
408 #endif // ACCESSIBILITY
410 default: {
411 // Unknown messages only are logged in debug builds and sent to
412 // DefWindowProc.
413 if (uMsg && uMsg == mozilla::widget::sAppShellGoannaMsgId) {
414 // Widget's registered native event callback
415 deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
420 // No deferred message was created and we land here, this is an
421 // unhandled message.
422 if (!deferred) {
423 DumpNeuteredMessage(hwnd, uMsg);
424 return DefWindowProc(hwnd, uMsg, wParam, lParam);
427 // Create the deferred message array if it doesn't exist already.
428 if (!gDeferredMessages) {
429 gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20);
430 NS_ASSERTION(gDeferredMessages, "Out of memory!");
433 // Save for later. The array takes ownership of |deferred|.
434 gDeferredMessages->AppendElement(deferred);
435 return res;
438 } // anonymous namespace
440 // We need the pointer value of this in PluginInstanceChild.
441 LRESULT CALLBACK
442 NeuteredWindowProc(HWND hwnd,
443 UINT uMsg,
444 WPARAM wParam,
445 LPARAM lParam)
447 WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
448 if (!oldWndProc) {
449 // We should really never ever get here.
450 NS_ERROR("No old wndproc!");
451 return DefWindowProc(hwnd, uMsg, wParam, lParam);
454 // See if we care about this message. We may either ignore it, send it to
455 // DefWindowProc, or defer it for later.
456 return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam);
459 namespace {
461 static bool
462 WindowIsDeferredWindow(HWND hWnd)
464 if (!IsWindow(hWnd)) {
465 NS_WARNING("Window has died!");
466 return false;
469 char16_t buffer[256] = { 0 };
470 int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1);
471 if (length <= 0) {
472 NS_WARNING("Failed to get class name!");
473 return false;
476 #if defined(ACCESSIBILITY)
477 // Tab content creates a window that responds to accessible WM_GETOBJECT
478 // calls. This window can safely be ignored.
479 if (::GetPropW(hWnd, kPropNameTabContent)) {
480 return false;
482 #endif
484 // Common mozilla windows we must defer messages to.
485 nsDependentString className(buffer, length);
486 if (StringBeginsWith(className, NS_LITERAL_STRING("Mozilla")) ||
487 StringBeginsWith(className, NS_LITERAL_STRING("Goanna")) ||
488 className.EqualsLiteral("nsToolkitClass") ||
489 className.EqualsLiteral("nsAppShell:EventWindowClass")) {
490 return true;
493 // Plugin windows that can trigger ipc calls in child:
494 // 'ShockwaveFlashFullScreen' - flash fullscreen window
495 // 'QTNSHIDDEN' - QuickTime
496 // 'AGFullScreenWinClass' - silverlight fullscreen window
497 if (className.EqualsLiteral("ShockwaveFlashFullScreen") ||
498 className.EqualsLiteral("QTNSHIDDEN") ||
499 className.EqualsLiteral("AGFullScreenWinClass")) {
500 SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
501 return true;
504 // Google Earth bridging msg window between the plugin instance and a separate
505 // earth process. The earth process can trigger a plugin incall on the browser
506 // at any time, which is badness if the instance is already making an incall.
507 if (className.EqualsLiteral("__geplugin_bridge_window__")) {
508 SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
509 return true;
512 // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the
513 // toolkit app's name. It's pretty expensive to calculate this so we only try
514 // once.
515 if (gAppMessageWindowNameLength == 0) {
516 nsCOMPtr<nsIXULAppInfo> appInfo =
517 do_GetService("@mozilla.org/xre/app-info;1");
518 if (appInfo) {
519 nsAutoCString appName;
520 if (NS_SUCCEEDED(appInfo->GetName(appName))) {
521 appName.AppendLiteral("MessageWindow");
522 nsDependentString windowName(gAppMessageWindowName);
523 CopyUTF8toUTF16(appName, windowName);
524 gAppMessageWindowNameLength = windowName.Length();
528 // Don't try again if that failed.
529 if (gAppMessageWindowNameLength == 0) {
530 gAppMessageWindowNameLength = -1;
534 if (gAppMessageWindowNameLength != -1 &&
535 className.Equals(nsDependentString(gAppMessageWindowName,
536 gAppMessageWindowNameLength))) {
537 return true;
540 return false;
543 bool
544 NeuterWindowProcedure(HWND hWnd)
546 if (!WindowIsDeferredWindow(hWnd)) {
547 // Some other kind of window, skip.
548 return false;
551 NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!");
553 // It's possible to get nullptr out of SetWindowLongPtr, and the only way to
554 // know if that's a valid old value is to use GetLastError. Clear the error
555 // here so we can tell.
556 SetLastError(ERROR_SUCCESS);
558 LONG_PTR currentWndProc =
559 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc);
560 if (!currentWndProc) {
561 if (ERROR_SUCCESS == GetLastError()) {
562 // No error, so we set something and must therefore reset it.
563 SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
565 return false;
568 NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
569 "This shouldn't be possible!");
571 if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
572 // Cleanup
573 NS_WARNING("SetProp failed!");
574 SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
575 RemovePropW(hWnd, kOldWndProcProp);
576 RemovePropW(hWnd, k3rdPartyWindowProp);
577 return false;
580 return true;
583 void
584 RestoreWindowProcedure(HWND hWnd)
586 NS_ASSERTION(WindowIsDeferredWindow(hWnd),
587 "Not a deferred window, this shouldn't be in our list!");
588 LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp);
589 if (oldWndProc) {
590 NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
591 "This shouldn't be possible!");
593 DebugOnly<LONG_PTR> currentWndProc =
594 SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
595 NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
596 "This should never be switched out from under us!");
598 RemovePropW(hWnd, kOldWndProcProp);
599 RemovePropW(hWnd, k3rdPartyWindowProp);
602 LRESULT CALLBACK
603 CallWindowProcedureHook(int nCode,
604 WPARAM wParam,
605 LPARAM lParam)
607 if (nCode >= 0) {
608 NS_ASSERTION(gNeuteredWindows, "This should never be null!");
610 HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
612 if (!gNeuteredWindows->Contains(hWnd) && NeuterWindowProcedure(hWnd)) {
613 if (!gNeuteredWindows->AppendElement(hWnd)) {
614 NS_ERROR("Out of memory!");
615 RestoreWindowProcedure(hWnd);
619 return CallNextHookEx(nullptr, nCode, wParam, lParam);
622 inline void
623 AssertWindowIsNotNeutered(HWND hWnd)
625 #ifdef DEBUG
626 // Make sure our neutered window hook isn't still in place.
627 LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
628 NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!");
629 #endif
632 void
633 UnhookNeuteredWindows()
635 if (!gNeuteredWindows)
636 return;
637 uint32_t count = gNeuteredWindows->Length();
638 for (uint32_t index = 0; index < count; index++) {
639 RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
641 gNeuteredWindows->Clear();
644 // This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
645 // value for GetTickCount(), which is something like 50 days). It uses the
646 // cheapest (and least accurate) method supported by Windows 2000.
648 struct TimeoutData
650 DWORD startTicks;
651 DWORD targetTicks;
654 void
655 InitTimeoutData(TimeoutData* aData,
656 int32_t aTimeoutMs)
658 aData->startTicks = GetTickCount();
659 if (!aData->startTicks) {
660 // How unlikely is this!
661 aData->startTicks++;
663 aData->targetTicks = aData->startTicks + aTimeoutMs;
667 bool
668 TimeoutHasExpired(const TimeoutData& aData)
670 if (!aData.startTicks) {
671 return false;
674 DWORD now = GetTickCount();
676 if (aData.targetTicks < aData.startTicks) {
677 // Overflow
678 return now < aData.startTicks && now >= aData.targetTicks;
680 return now >= aData.targetTicks;
683 } // anonymous namespace
685 namespace mozilla {
686 namespace ipc {
687 namespace windows {
689 void
690 InitUIThread()
692 // If we aren't setup before a call to NotifyWorkerThread, we'll hang
693 // on startup.
694 if (!gUIThreadId) {
695 gUIThreadId = GetCurrentThreadId();
698 MOZ_ASSERT(gUIThreadId);
699 MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
700 "Called InitUIThread multiple times on different threads!");
702 if (!gWinEventHook) {
703 gWinEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
704 NULL, &WinEventHook, GetCurrentProcessId(),
705 gUIThreadId, WINEVENT_OUTOFCONTEXT);
707 // We need to execute this after setting the hook in case the OLE window
708 // already existed.
709 gCOMWindow = FindCOMWindow();
711 MOZ_ASSERT(gWinEventHook);
714 } // namespace windows
715 } // namespace ipc
716 } // namespace mozilla
718 // See SpinInternalEventLoop below
719 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
720 : mInterrupt(interrupt)
721 , mSpinNestedEvents(false)
722 , mListenerNotified(false)
723 , mChannel(channel)
724 , mPrev(mChannel->mTopFrame)
725 , mStaticPrev(sStaticTopFrame)
727 // Only track stack frames when Windows message deferral behavior
728 // is request for the channel.
729 if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
730 return;
733 mChannel->mTopFrame = this;
734 sStaticTopFrame = this;
736 if (!mStaticPrev) {
737 NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
738 gNeuteredWindows = new nsAutoTArray<HWND, 20>();
739 NS_ASSERTION(gNeuteredWindows, "Out of memory!");
743 MessageChannel::SyncStackFrame::~SyncStackFrame()
745 if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
746 return;
749 NS_ASSERTION(this == mChannel->mTopFrame,
750 "Mismatched interrupt stack frames");
751 NS_ASSERTION(this == sStaticTopFrame,
752 "Mismatched static Interrupt stack frames");
754 mChannel->mTopFrame = mPrev;
755 sStaticTopFrame = mStaticPrev;
757 if (!mStaticPrev) {
758 NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
759 delete gNeuteredWindows;
760 gNeuteredWindows = nullptr;
764 MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
766 // nsAppShell's notification that goanna events are being processed.
767 // If we are here and there is an Interrupt Incall active, we are spinning
768 // a nested goanna event loop. In which case the remote process needs
769 // to know about it.
770 void /* static */
771 MessageChannel::NotifyGoannaEventDispatch()
773 // sStaticTopFrame is only valid for Interrupt channels
774 if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
775 return;
777 sStaticTopFrame->mListenerNotified = true;
778 MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
779 channel->Listener()->ProcessRemoteNativeEventsInInterruptCall();
782 // invoked by the module that receives the spin event loop
783 // message.
784 void
785 MessageChannel::ProcessNativeEventsInInterruptCall()
787 NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
788 "Shouldn't be on a non-main thread in here!");
789 if (!mTopFrame) {
790 NS_ERROR("Spin logic error: no Interrupt frame");
791 return;
794 mTopFrame->mSpinNestedEvents = true;
797 // Spin loop is called in place of WaitFor*Notify when modal ui is being shown
798 // in a child. There are some intricacies in using it however. Spin loop is
799 // enabled for a particular Interrupt frame by the client calling
800 // MessageChannel::ProcessNativeEventsInInterrupt().
801 // This call can be nested for multiple Interrupt frames in a single plugin or
802 // multiple unrelated plugins.
803 void
804 MessageChannel::SpinInternalEventLoop()
806 if (mozilla::PaintTracker::IsPainting()) {
807 NS_RUNTIMEABORT("Don't spin an event loop while painting.");
810 NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents,
811 "Spinning incorrectly");
813 // Nested windows event loop we trigger when the child enters into modal
814 // event loops.
816 // Note, when we return, we always reset the notify worker event. So there's
817 // no need to reset it on return here.
819 do {
820 MSG msg = { 0 };
822 // Don't get wrapped up in here if the child connection dies.
824 MonitorAutoLock lock(*mMonitor);
825 if (!Connected()) {
826 return;
830 // Retrieve window or thread messages
831 if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
832 // The child UI should have been destroyed before the app is closed, in
833 // which case, we should never get this here.
834 if (msg.message == WM_QUIT) {
835 NS_ERROR("WM_QUIT received in SpinInternalEventLoop!");
836 } else {
837 TranslateMessage(&msg);
838 ::DispatchMessageW(&msg);
839 return;
843 // Note, give dispatching windows events priority over checking if
844 // mEvent is signaled, otherwise heavy ipc traffic can cause jittery
845 // playback of video. We'll exit out on each disaptch above, so ipc
846 // won't get starved.
848 // Wait for UI events or a signal from the io thread.
849 DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
850 QS_ALLINPUT);
851 if (result == WAIT_OBJECT_0) {
852 // Our NotifyWorkerThread event was signaled
853 return;
855 } while (true);
858 static inline bool
859 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
861 return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
862 (aTimeout <= (PR_IntervalNow() - aStart));
865 bool
866 MessageChannel::WaitForSyncNotify()
868 mMonitor->AssertCurrentThreadOwns();
870 MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
872 // Use a blocking wait if this channel does not require
873 // Windows message deferral behavior.
874 if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
875 PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
876 PR_INTERVAL_NO_TIMEOUT :
877 PR_MillisecondsToInterval(mTimeoutMs);
878 PRIntervalTime waitStart = 0;
880 if (timeout != PR_INTERVAL_NO_TIMEOUT) {
881 waitStart = PR_IntervalNow();
884 MOZ_ASSERT(!mIsSyncWaitingOnNonMainThread);
885 mIsSyncWaitingOnNonMainThread = true;
887 mMonitor->Wait(timeout);
889 MOZ_ASSERT(mIsSyncWaitingOnNonMainThread);
890 mIsSyncWaitingOnNonMainThread = false;
892 // If the timeout didn't expire, we know we received an event. The
893 // converse is not true.
894 return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ?
895 false : IsTimeoutExpired(waitStart, timeout));
898 NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
899 "Shouldn't be here for channels that don't use message deferral!");
900 NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
901 "Top frame is not a sync frame!");
903 MonitorAutoUnlock unlock(*mMonitor);
905 bool timedout = false;
907 UINT_PTR timerId = 0;
908 TimeoutData timeoutData = { 0 };
910 if (mTimeoutMs != kNoTimeout) {
911 InitTimeoutData(&timeoutData, mTimeoutMs);
913 // We only do this to ensure that we won't get stuck in
914 // MsgWaitForMultipleObjects below.
915 timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
916 NS_ASSERTION(timerId, "SetTimer failed!");
919 // Setup deferred processing of native events while we wait for a response.
920 NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
921 "Shouldn't be pumping already!");
923 MessageChannel::SetIsPumpingMessages(true);
924 HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
925 nullptr, gUIThreadId);
926 NS_ASSERTION(windowHook, "Failed to set hook!");
929 while (1) {
930 MSG msg = { 0 };
931 // Don't get wrapped up in here if the child connection dies.
933 MonitorAutoLock lock(*mMonitor);
934 if (!Connected()) {
935 break;
939 // Wait until we have a message in the queue. MSDN docs are a bit unclear
940 // but it seems that windows from two different threads (and it should be
941 // noted that a thread in another process counts as a "different thread")
942 // will implicitly have their message queues attached if they are parented
943 // to one another. This wait call, then, will return for a message
944 // delivered to *either* thread.
945 DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
946 QS_ALLINPUT);
947 if (result == WAIT_OBJECT_0) {
948 // Our NotifyWorkerThread event was signaled
949 ResetEvent(mEvent);
950 break;
951 } else
952 if (result != (WAIT_OBJECT_0 + 1)) {
953 NS_ERROR("Wait failed!");
954 break;
957 if (TimeoutHasExpired(timeoutData)) {
958 // A timeout was specified and we've passed it. Break out.
959 timedout = true;
960 break;
963 // The only way to know on which thread the message was delivered is to
964 // use some logic on the return values of GetQueueStatus and PeekMessage.
965 // PeekMessage will return false if there are no "queued" messages, but it
966 // will run all "nonqueued" messages before returning. So if PeekMessage
967 // returns false and there are no "nonqueued" messages that were run then
968 // we know that the message we woke for was intended for a window on
969 // another thread.
970 bool haveSentMessagesPending =
971 (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
973 // Either of the PeekMessage calls below will actually process all
974 // "nonqueued" messages that are pending before returning. If we have
975 // "nonqueued" messages pending then we should have switched out all the
976 // window procedures above. In that case this PeekMessage call won't
977 // actually cause any mozilla code (or plugin code) to run.
979 // We have to manually pump all COM messages *after* looking at the queue
980 // queue status but before yielding our thread below.
981 if (gCOMWindow) {
982 if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
983 TranslateMessage(&msg);
984 ::DispatchMessageW(&msg);
988 // If the following PeekMessage call fails to return a message for us (and
989 // returns false) and we didn't run any "nonqueued" messages then we must
990 // have woken up for a message designated for a window in another thread.
991 // If we loop immediately then we could enter a tight loop, so we'll give
992 // up our time slice here to let the child process its message.
993 if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
994 !haveSentMessagesPending) {
995 // Message was for child, we should wait a bit.
996 SwitchToThread();
1001 // Unhook the neutered window procedure hook.
1002 UnhookWindowsHookEx(windowHook);
1004 // Unhook any neutered windows procedures so messages can be delivered
1005 // normally.
1006 UnhookNeuteredWindows();
1008 // Before returning we need to set a hook to run any deferred messages that
1009 // we received during the IPC call. The hook will unset itself as soon as
1010 // someone else calls GetMessage, PeekMessage, or runs code that generates
1011 // a "nonqueued" message.
1012 ScheduleDeferredMessageRun();
1014 if (timerId) {
1015 KillTimer(nullptr, timerId);
1018 MessageChannel::SetIsPumpingMessages(false);
1020 return WaitResponse(timedout);
1023 bool
1024 MessageChannel::WaitForInterruptNotify()
1026 mMonitor->AssertCurrentThreadOwns();
1028 MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
1030 // Re-use sync notification wait code if this channel does not require
1031 // Windows message deferral behavior.
1032 if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
1033 return WaitForSyncNotify();
1036 if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
1037 // There is currently no way to recover from this condition.
1038 NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
1041 NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
1042 "Shouldn't be here for channels that don't use message deferral!");
1043 NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
1044 "Top frame is not a sync frame!");
1046 MonitorAutoUnlock unlock(*mMonitor);
1048 bool timedout = false;
1050 UINT_PTR timerId = 0;
1051 TimeoutData timeoutData = { 0 };
1053 // windowHook is used as a flag variable for the loop below: if it is set
1054 // and we start to spin a nested event loop, we need to clear the hook and
1055 // process deferred/pending messages.
1056 // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
1057 HHOOK windowHook = nullptr;
1059 while (1) {
1060 NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
1061 "windowHook out of sync with reality");
1063 if (mTopFrame->mSpinNestedEvents) {
1064 if (windowHook) {
1065 UnhookWindowsHookEx(windowHook);
1066 windowHook = nullptr;
1068 if (timerId) {
1069 KillTimer(nullptr, timerId);
1070 timerId = 0;
1073 // Used by widget to assert on incoming native events
1074 MessageChannel::SetIsPumpingMessages(false);
1076 // Unhook any neutered windows procedures so messages can be delievered
1077 // normally.
1078 UnhookNeuteredWindows();
1080 // Send all deferred "nonqueued" message to the intended receiver.
1081 // We're dropping into SpinInternalEventLoop so we should be fairly
1082 // certain these will get delivered soohn.
1083 ScheduleDeferredMessageRun();
1085 SpinInternalEventLoop();
1086 ResetEvent(mEvent);
1087 return true;
1090 if (!windowHook) {
1091 MessageChannel::SetIsPumpingMessages(true);
1092 windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
1093 nullptr, gUIThreadId);
1094 NS_ASSERTION(windowHook, "Failed to set hook!");
1096 NS_ASSERTION(!timerId, "Timer already initialized?");
1098 if (mTimeoutMs != kNoTimeout) {
1099 InitTimeoutData(&timeoutData, mTimeoutMs);
1100 timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
1101 NS_ASSERTION(timerId, "SetTimer failed!");
1105 MSG msg = { 0 };
1107 // Don't get wrapped up in here if the child connection dies.
1109 MonitorAutoLock lock(*mMonitor);
1110 if (!Connected()) {
1111 break;
1115 DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
1116 QS_ALLINPUT);
1117 if (result == WAIT_OBJECT_0) {
1118 // Our NotifyWorkerThread event was signaled
1119 ResetEvent(mEvent);
1120 break;
1121 } else
1122 if (result != (WAIT_OBJECT_0 + 1)) {
1123 NS_ERROR("Wait failed!");
1124 break;
1127 if (TimeoutHasExpired(timeoutData)) {
1128 // A timeout was specified and we've passed it. Break out.
1129 timedout = true;
1130 break;
1133 // See MessageChannel's WaitFor*Notify for details.
1134 bool haveSentMessagesPending =
1135 (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
1137 // Run all COM messages *after* looking at the queue status.
1138 if (gCOMWindow) {
1139 if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
1140 TranslateMessage(&msg);
1141 ::DispatchMessageW(&msg);
1145 // PeekMessage markes the messages as "old" so that they don't wake up
1146 // MsgWaitForMultipleObjects every time.
1147 if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
1148 !haveSentMessagesPending) {
1149 // Message was for child, we should wait a bit.
1150 SwitchToThread();
1154 if (windowHook) {
1155 // Unhook the neutered window procedure hook.
1156 UnhookWindowsHookEx(windowHook);
1158 // Unhook any neutered windows procedures so messages can be delivered
1159 // normally.
1160 UnhookNeuteredWindows();
1162 // Before returning we need to set a hook to run any deferred messages that
1163 // we received during the IPC call. The hook will unset itself as soon as
1164 // someone else calls GetMessage, PeekMessage, or runs code that generates
1165 // a "nonqueued" message.
1166 ScheduleDeferredMessageRun();
1168 if (timerId) {
1169 KillTimer(nullptr, timerId);
1173 MessageChannel::SetIsPumpingMessages(false);
1175 return WaitResponse(timedout);
1178 void
1179 MessageChannel::NotifyWorkerThread()
1181 mMonitor->AssertCurrentThreadOwns();
1183 if (mIsSyncWaitingOnNonMainThread) {
1184 mMonitor->Notify();
1185 return;
1188 NS_ASSERTION(mEvent, "No signal event to set, this is really bad!");
1189 if (!SetEvent(mEvent)) {
1190 NS_WARNING("Failed to set NotifyWorkerThread event!");
1194 void
1195 DeferredSendMessage::Run()
1197 AssertWindowIsNotNeutered(hWnd);
1198 if (!IsWindow(hWnd)) {
1199 NS_ERROR("Invalid window!");
1200 return;
1203 WNDPROC wndproc =
1204 reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
1205 if (!wndproc) {
1206 NS_ERROR("Invalid window procedure!");
1207 return;
1210 CallWindowProc(wndproc, hWnd, message, wParam, lParam);
1213 void
1214 DeferredRedrawMessage::Run()
1216 AssertWindowIsNotNeutered(hWnd);
1217 if (!IsWindow(hWnd)) {
1218 NS_ERROR("Invalid window!");
1219 return;
1222 #ifdef DEBUG
1223 BOOL ret =
1224 #endif
1225 RedrawWindow(hWnd, nullptr, nullptr, flags);
1226 NS_ASSERTION(ret, "RedrawWindow failed!");
1229 DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd)
1231 mWnd = aHWnd;
1232 if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) {
1233 memset(&mUpdateRect, 0, sizeof(RECT));
1234 return;
1236 ValidateRect(mWnd, &mUpdateRect);
1239 void
1240 DeferredUpdateMessage::Run()
1242 AssertWindowIsNotNeutered(mWnd);
1243 if (!IsWindow(mWnd)) {
1244 NS_ERROR("Invalid window!");
1245 return;
1248 InvalidateRect(mWnd, &mUpdateRect, FALSE);
1249 #ifdef DEBUG
1250 BOOL ret =
1251 #endif
1252 UpdateWindow(mWnd);
1253 NS_ASSERTION(ret, "UpdateWindow failed!");
1256 DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd,
1257 UINT aMessage,
1258 WPARAM aWParam,
1259 LPARAM aLParam)
1260 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1262 NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!");
1263 if (aLParam) {
1264 lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam));
1265 lParam = reinterpret_cast<LPARAM>(lParamString);
1267 else {
1268 lParamString = nullptr;
1269 lParam = 0;
1273 DeferredSettingChangeMessage::~DeferredSettingChangeMessage()
1275 free(lParamString);
1278 DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd,
1279 LPARAM aLParam,
1280 bool aForCalcSize,
1281 WPARAM aWParam)
1283 if (aForCalcSize) {
1284 if (aWParam) {
1285 NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam);
1286 memcpy(&windowPos, arg->lppos, sizeof(windowPos));
1288 NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
1290 else {
1291 RECT* arg = reinterpret_cast<RECT*>(aLParam);
1292 windowPos.hwnd = aHWnd;
1293 windowPos.hwndInsertAfter = nullptr;
1294 windowPos.x = arg->left;
1295 windowPos.y = arg->top;
1296 windowPos.cx = arg->right - arg->left;
1297 windowPos.cy = arg->bottom - arg->top;
1299 NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top,
1300 "Negative width or height!");
1302 windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1303 SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING;
1305 else {
1306 // Not for WM_NCCALCSIZE
1307 WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam);
1308 memcpy(&windowPos, arg, sizeof(windowPos));
1310 NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
1312 // Windows sends in some private flags sometimes that we can't simply copy.
1313 // Filter here.
1314 UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME |
1315 SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE |
1316 SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
1317 SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE |
1318 SWP_NOZORDER | SWP_SHOWWINDOW;
1319 windowPos.flags &= mask;
1323 void
1324 DeferredWindowPosMessage::Run()
1326 AssertWindowIsNotNeutered(windowPos.hwnd);
1327 if (!IsWindow(windowPos.hwnd)) {
1328 NS_ERROR("Invalid window!");
1329 return;
1332 if (!IsWindow(windowPos.hwndInsertAfter)) {
1333 NS_WARNING("ZOrder change cannot be honored");
1334 windowPos.hwndInsertAfter = 0;
1335 windowPos.flags |= SWP_NOZORDER;
1338 #ifdef DEBUG
1339 BOOL ret =
1340 #endif
1341 SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x,
1342 windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags);
1343 NS_ASSERTION(ret, "SetWindowPos failed!");
1346 DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd,
1347 UINT aMessage,
1348 WPARAM aWParam,
1349 LPARAM aLParam)
1350 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1352 NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!");
1354 COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam);
1355 NS_ASSERTION(source, "Should never be null!");
1357 copyData.dwData = source->dwData;
1358 copyData.cbData = source->cbData;
1360 if (source->cbData) {
1361 copyData.lpData = malloc(source->cbData);
1362 if (copyData.lpData) {
1363 memcpy(copyData.lpData, source->lpData, source->cbData);
1365 else {
1366 NS_ERROR("Out of memory?!");
1367 copyData.cbData = 0;
1370 else {
1371 copyData.lpData = nullptr;
1374 lParam = reinterpret_cast<LPARAM>(&copyData);
1377 DeferredCopyDataMessage::~DeferredCopyDataMessage()
1379 free(copyData.lpData);
1382 DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd,
1383 WPARAM aWParam,
1384 LPARAM aLParam)
1385 : hWnd(aHWnd)
1387 index = static_cast<int>(aWParam);
1388 style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew;
1391 void
1392 DeferredStyleChangeMessage::Run()
1394 SetWindowLongPtr(hWnd, index, style);
1397 DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd,
1398 UINT aMessage,
1399 WPARAM aWParam,
1400 LPARAM aLParam)
1401 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1403 NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!");
1406 void
1407 DeferredSetIconMessage::Run()
1409 AssertWindowIsNotNeutered(hWnd);
1410 if (!IsWindow(hWnd)) {
1411 NS_ERROR("Invalid window!");
1412 return;
1415 WNDPROC wndproc =
1416 reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
1417 if (!wndproc) {
1418 NS_ERROR("Invalid window procedure!");
1419 return;
1422 HICON hOld = reinterpret_cast<HICON>(
1423 CallWindowProc(wndproc, hWnd, message, wParam, lParam));
1424 if (hOld) {
1425 DestroyIcon(hOld);