1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 "mozilla/DebugOnly.h"
8 
9 #include "WindowsMessageLoop.h"
10 #include "Neutering.h"
11 #include "MessageChannel.h"
12 
13 #include "nsAutoPtr.h"
14 #include "nsServiceManagerUtils.h"
15 #include "nsString.h"
16 #include "nsIXULAppInfo.h"
17 #include "WinUtils.h"
18 
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/ipc/ProtocolUtils.h"
21 #include "mozilla/PaintTracker.h"
22 #include "mozilla/WindowsVersion.h"
23 
24 using namespace mozilla;
25 using namespace mozilla::ipc;
26 using namespace mozilla::ipc::windows;
27 
28 /**
flow_exit(const char * strp)29  * The Windows-only code below exists to solve a general problem with deadlocks
30  * that we experience when sending synchronous IPC messages to processes that
31  * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous
32  * messages between parent and child HWNDs in multiple circumstances (e.g.
33  * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled
34  * by different threads or different processes. Thus we can very easily end up
35  * in a deadlock by a call stack like the following:
36  *
37  * Process A:
38  *   - CreateWindow(...) creates a "parent" HWND.
39  *   - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent"
40  *         HWND over to Process B. Process A blocks until a response is received
41  *         from Process B.
42  *
43  * Process B:
44  *   - RecvCreateWidget(HWND) gets the "parent" HWND from Process A.
45  *   - CreateWindow(..., HWND) creates a "child" HWND with the parent from
46  *         process A.
47  *   - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent
48  *         synchronously to Process A. Process B blocks until a response is
49  *         received from Process A. Process A, however, is blocked and cannot
50  *         process the message. Both processes are deadlocked.
51  *
52  * The example above has a few different workarounds (e.g. setting the
53  * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is
54  * persists. Once two HWNDs are parented we must not block their owning
55  * threads when manipulating either HWND.
56  *
57  * Windows requires any application that hosts native HWNDs to always process
58  * messages or risk deadlock. Given our architecture the only way to meet
59  * Windows' requirement and allow for synchronous IPC messages is to pump a
60  * miniature message loop during a sync IPC call. We avoid processing any
61  * queued messages during the loop (with one exception, see below), but
62  * "nonqueued" messages (see
63  * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the
64  * section "Nonqueued messages") cannot be avoided. Those messages are trapped
65  * in a special window procedure where we can either ignore the message or
66  * process it in some fashion.
67  *
68  * Queued and "non-queued" messages will be processed during Interrupt calls if
69  * modal UI related api calls block an Interrupt in-call in the child. To prevent
70  * windows from freezing, and to allow concurrent processing of critical
71  * events (such as painting), we spin a native event dispatch loop while
72  * these in-calls are blocked.
73  */
74 
75 #if defined(ACCESSIBILITY)
76 // pulled from accessibility's win utils
77 extern const wchar_t* kPropNameTabContent;
78 #endif
flow_init(const char * opt_argp,void * userdata)79 
80 // widget related message id constants we need to defer
81 namespace mozilla {
82 namespace widget {
83 extern UINT sAppShellGeckoMsgId;
84 }
85 }
86 
87 namespace {
88 
89 const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
90 const wchar_t k3rdPartyWindowProp[] = L"Mozilla3rdPartyWindow";
91 
92 // This isn't defined before Windows XP.
93 enum { WM_XP_THEMECHANGED = 0x031A };
94 
95 char16_t gAppMessageWindowName[256] = { 0 };
96 int32_t gAppMessageWindowNameLength = 0;
97 
98 nsTArray<HWND>* gNeuteredWindows = nullptr;
99 
100 typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
101 DeferredMessageArray* gDeferredMessages = nullptr;
102 
103 HHOOK gDeferredGetMsgHook = nullptr;
104 HHOOK gDeferredCallWndProcHook = nullptr;
105 
106 DWORD gUIThreadId = 0;
107 HWND gCOMWindow = 0;
108 // Once initialized, gWinEventHook is never unhooked. We save the handle so
flow_register(const void * key _U_,void * value,void * userdata _U_)109 // that we can check whether or not the hook is initialized.
110 HWINEVENTHOOK gWinEventHook = nullptr;
111 const wchar_t kCOMWindowClassName[] = L"OleMainThreadWndClass";
112 
113 // WM_GETOBJECT id pulled from uia headers
114 #define MOZOBJID_UIAROOT -25
115 
116 HWND
117 FindCOMWindow()
118 {
119   MOZ_ASSERT(gUIThreadId);
120 
121   HWND last = 0;
122   while ((last = FindWindowExW(HWND_MESSAGE, last, kCOMWindowClassName, NULL))) {
123     if (GetWindowThreadProcessId(last, NULL) == gUIThreadId) {
124       return last;
125     }
126   }
127 
128   return (HWND)0;
129 }
130 
register_tap_listener_flow(void)131 void CALLBACK
132 WinEventHook(HWINEVENTHOOK aWinEventHook, DWORD aEvent, HWND aHwnd,
133              LONG aIdObject, LONG aIdChild, DWORD aEventThread,
134              DWORD aMsEventTime)
135 {
136   MOZ_ASSERT(aWinEventHook == gWinEventHook);
137   MOZ_ASSERT(gUIThreadId == aEventThread);
138   switch (aEvent) {
139     case EVENT_OBJECT_CREATE: {
140       if (aIdObject != OBJID_WINDOW || aIdChild != CHILDID_SELF) {
141         // Not an event we're interested in
142         return;
143       }
144       wchar_t classBuf[256] = {0};
145       int result = ::GetClassNameW(aHwnd, classBuf,
146                                    MOZ_ARRAY_LENGTH(classBuf));
147       if (result != (MOZ_ARRAY_LENGTH(kCOMWindowClassName) - 1) ||
148           wcsncmp(kCOMWindowClassName, classBuf, result)) {
149         // Not a class we're interested in
150         return;
151       }
152       MOZ_ASSERT(FindCOMWindow() == aHwnd);
153       gCOMWindow = aHwnd;
154       break;
155     }
156     case EVENT_OBJECT_DESTROY: {
157       if (aHwnd == gCOMWindow && aIdObject == OBJID_WINDOW) {
158         MOZ_ASSERT(aIdChild == CHILDID_SELF);
159         gCOMWindow = 0;
160       }
161       break;
162     }
163     default: {
164       return;
165     }
166   }
167 }
168 
169 LRESULT CALLBACK
170 DeferredMessageHook(int nCode,
171                     WPARAM wParam,
172                     LPARAM lParam)
173 {
174   // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
175   //     WH_GETMESSAGE hook, but they have different parameters. We don't
176   //     use any of them except nCode which has the same meaning.
177 
178   // Only run deferred messages if all of these conditions are met:
179   //   1. The |nCode| indicates that this hook should do something.
180   //   2. We have deferred messages to run.
181   //   3. We're not being called from the PeekMessage within the WaitFor*Notify
182   //      function (indicated with MessageChannel::IsPumpingMessages). We really
183   //      only want to run after returning to the main event loop.
184   if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) {
185     NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
186                  "These hooks must be set if we're being called!");
187     NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
188 
189     // Unset hooks first, in case we reenter below.
190     UnhookWindowsHookEx(gDeferredGetMsgHook);
191     UnhookWindowsHookEx(gDeferredCallWndProcHook);
192     gDeferredGetMsgHook = 0;
193     gDeferredCallWndProcHook = 0;
194 
195     // Unset the global and make sure we delete it when we're done here.
196     nsAutoPtr<DeferredMessageArray> messages(gDeferredMessages);
197     gDeferredMessages = nullptr;
198 
199     // Run all the deferred messages in order.
200     uint32_t count = messages->Length();
201     for (uint32_t index = 0; index < count; index++) {
202       messages->ElementAt(index)->Run();
203     }
204   }
205 
206   // Always call the next hook.
207   return CallNextHookEx(nullptr, nCode, wParam, lParam);
208 }
209 
210 void
211 ScheduleDeferredMessageRun()
212 {
213   if (gDeferredMessages &&
214       !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) {
215     NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
216 
217     gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook,
218                                              nullptr, gUIThreadId);
219     gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,
220                                                   DeferredMessageHook, nullptr,
221                                                   gUIThreadId);
222     NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
223                  "Failed to set hooks!");
224   }
225 }
226 
227 static void
228 DumpNeuteredMessage(HWND hwnd, UINT uMsg)
229 {
230 #ifdef DEBUG
231   nsAutoCString log("Received \"nonqueued\" ");
232   // classify messages
233   if (uMsg < WM_USER) {
234     int idx = 0;
235     while (mozilla::widget::gAllEvents[idx].mId != (long)uMsg &&
236            mozilla::widget::gAllEvents[idx].mStr != nullptr) {
237       idx++;
238     }
239     if (mozilla::widget::gAllEvents[idx].mStr) {
240       log.AppendPrintf("ui message \"%s\"", mozilla::widget::gAllEvents[idx].mStr);
241     } else {
242       log.AppendPrintf("ui message (0x%X)", uMsg);
243     }
244   } else if (uMsg >= WM_USER && uMsg < WM_APP) {
245     log.AppendPrintf("WM_USER message (0x%X)", uMsg);
246   } else if (uMsg >= WM_APP && uMsg < 0xC000) {
247     log.AppendPrintf("WM_APP message (0x%X)", uMsg);
248   } else if (uMsg >= 0xC000 && uMsg < 0x10000) {
249     log.AppendPrintf("registered windows message (0x%X)", uMsg);
250   } else {
251     log.AppendPrintf("system message (0x%X)", uMsg);
252   }
253 
254   log.AppendLiteral(" during a synchronous IPC message for window ");
255   log.AppendPrintf("0x%X", hwnd);
256 
257   wchar_t className[256] = { 0 };
258   if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
259     log.AppendLiteral(" (\"");
260     log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
261     log.AppendLiteral("\")");
262   }
263 
264   log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
265                     "window procedure.");
266   NS_ERROR(log.get());
267 #endif
268 }
269 
270 LRESULT
271 ProcessOrDeferMessage(HWND hwnd,
272                       UINT uMsg,
273                       WPARAM wParam,
274                       LPARAM lParam)
275 {
276   DeferredMessage* deferred = nullptr;
277 
278   // Most messages ask for 0 to be returned if the message is processed.
279   LRESULT res = 0;
280 
281   switch (uMsg) {
282     // Messages that can be deferred as-is. These must not contain pointers in
283     // their wParam or lParam arguments!
284     case WM_ACTIVATE:
285     case WM_ACTIVATEAPP:
286     case WM_CANCELMODE:
287     case WM_CAPTURECHANGED:
288     case WM_CHILDACTIVATE:
289     case WM_DESTROY:
290     case WM_ENABLE:
291     case WM_IME_NOTIFY:
292     case WM_IME_SETCONTEXT:
293     case WM_KILLFOCUS:
294     case WM_MOUSEWHEEL:
295     case WM_NCDESTROY:
296     case WM_PARENTNOTIFY:
297     case WM_SETFOCUS:
298     case WM_SYSCOMMAND:
299     case WM_DISPLAYCHANGE:
300     case WM_SHOWWINDOW: // Intentional fall-through.
301     case WM_XP_THEMECHANGED: {
302       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
303       break;
304     }
305 
306     case WM_DEVICECHANGE:
307     case WM_POWERBROADCAST:
308     case WM_NCACTIVATE: // Intentional fall-through.
309     case WM_SETCURSOR: {
310       // Friggin unconventional return value...
311       res = TRUE;
312       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
313       break;
314     }
315 
316     case WM_MOUSEACTIVATE: {
317       res = MA_NOACTIVATE;
318       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
319       break;
320     }
321 
322     // These messages need to use the RedrawWindow function to generate the
323     // right kind of message. We can't simply fake them as the MSDN docs say
324     // explicitly that paint messages should not be sent by an application.
325     case WM_ERASEBKGND: {
326       UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT |
327                    RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW;
328       deferred = new DeferredRedrawMessage(hwnd, flags);
329       break;
330     }
331 
332     // This message will generate a WM_PAINT message if there are invalid
333     // areas.
334     case WM_PAINT: {
335       deferred = new DeferredUpdateMessage(hwnd);
336       break;
337     }
338 
339     // This message holds a string in its lParam that we must copy.
340     case WM_SETTINGCHANGE: {
341       deferred = new DeferredSettingChangeMessage(hwnd, uMsg, wParam, lParam);
342       break;
343     }
344 
345     // These messages are faked via a call to SetWindowPos.
346     case WM_WINDOWPOSCHANGED: {
347       deferred = new DeferredWindowPosMessage(hwnd, lParam);
348       break;
349     }
350     case WM_NCCALCSIZE: {
351       deferred = new DeferredWindowPosMessage(hwnd, lParam, true, wParam);
352       break;
353     }
354 
355     case WM_COPYDATA: {
356       deferred = new DeferredCopyDataMessage(hwnd, uMsg, wParam, lParam);
357       res = TRUE;
358       break;
359     }
360 
361     case WM_STYLECHANGED: {
362       deferred = new DeferredStyleChangeMessage(hwnd, wParam, lParam);
363       break;
364     }
365 
366     case WM_SETICON: {
367       deferred = new DeferredSetIconMessage(hwnd, uMsg, wParam, lParam);
368       break;
369     }
370 
371     // Messages that are safe to pass to DefWindowProc go here.
372     case WM_ENTERIDLE:
373     case WM_GETICON:
374     case WM_NCPAINT: // (never trap nc paint events)
375     case WM_GETMINMAXINFO:
376     case WM_GETTEXT:
377     case WM_NCHITTEST:
378     case WM_STYLECHANGING:  // Intentional fall-through.
379     case WM_WINDOWPOSCHANGING:
380     case WM_GETTEXTLENGTH: {
381       return DefWindowProc(hwnd, uMsg, wParam, lParam);
382     }
383 
384     // Just return, prevents DefWindowProc from messaging the window
385     // syncronously with other events, which may be deferred. Prevents
386     // random shutdown of aero composition on the window.
387     case WM_SYNCPAINT:
388       return 0;
389 
390     // This message causes QuickTime to make re-entrant calls.
391     // Simply discarding it doesn't seem to hurt anything.
392     case WM_APP-1:
393       return 0;
394 
395     // We only support a query for our IAccessible or UIA pointers.
396     // This should be safe, and needs to be sync.
397 #if defined(ACCESSIBILITY)
398    case WM_GETOBJECT: {
399       if (!::GetPropW(hwnd, k3rdPartyWindowProp)) {
400         DWORD objId = static_cast<DWORD>(lParam);
401         if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT)) {
402           WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
403           if (oldWndProc) {
404             return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
405           }
406         }
407       }
408       return DefWindowProc(hwnd, uMsg, wParam, lParam);
409    }
410 #endif // ACCESSIBILITY
411 
412     default: {
413       // Unknown messages only are logged in debug builds and sent to
414       // DefWindowProc.
415       if (uMsg && uMsg == mozilla::widget::sAppShellGeckoMsgId) {
416         // Widget's registered native event callback
417         deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
418       }
419     }
420   }
421 
422   // No deferred message was created and we land here, this is an
423   // unhandled message.
424   if (!deferred) {
425     DumpNeuteredMessage(hwnd, uMsg);
426     return DefWindowProc(hwnd, uMsg, wParam, lParam);
427   }
428 
429   // Create the deferred message array if it doesn't exist already.
430   if (!gDeferredMessages) {
431     gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20);
432     NS_ASSERTION(gDeferredMessages, "Out of memory!");
433   }
434 
435   // Save for later. The array takes ownership of |deferred|.
436   gDeferredMessages->AppendElement(deferred);
437   return res;
438 }
439 
440 } // namespace
441 
442 // We need the pointer value of this in PluginInstanceChild.
443 LRESULT CALLBACK
444 NeuteredWindowProc(HWND hwnd,
445                    UINT uMsg,
446                    WPARAM wParam,
447                    LPARAM lParam)
448 {
449   WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
450   if (!oldWndProc) {
451     // We should really never ever get here.
452     NS_ERROR("No old wndproc!");
453     return DefWindowProc(hwnd, uMsg, wParam, lParam);
454   }
455 
456   // See if we care about this message. We may either ignore it, send it to
457   // DefWindowProc, or defer it for later.
458   return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam);
459 }
460 
461 namespace {
462 
463 static bool
464 WindowIsDeferredWindow(HWND hWnd)
465 {
466   if (!IsWindow(hWnd)) {
467     NS_WARNING("Window has died!");
468     return false;
469   }
470 
471   char16_t buffer[256] = { 0 };
472   int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1);
473   if (length <= 0) {
474     NS_WARNING("Failed to get class name!");
475     return false;
476   }
477 
478 #if defined(ACCESSIBILITY)
479   // Tab content creates a window that responds to accessible WM_GETOBJECT
480   // calls. This window can safely be ignored.
481   if (::GetPropW(hWnd, kPropNameTabContent)) {
482     return false;
483   }
484 #endif
485 
486   // Common mozilla windows we must defer messages to.
487   nsDependentString className(buffer, length);
488   if (StringBeginsWith(className, NS_LITERAL_STRING("Mozilla")) ||
489       StringBeginsWith(className, NS_LITERAL_STRING("Gecko")) ||
490       className.EqualsLiteral("nsToolkitClass") ||
491       className.EqualsLiteral("nsAppShell:EventWindowClass")) {
492     return true;
493   }
494 
495   // Plugin windows that can trigger ipc calls in child:
496   // 'ShockwaveFlashFullScreen' - flash fullscreen window
497   // 'QTNSHIDDEN' - QuickTime
498   // 'AGFullScreenWinClass' - silverlight fullscreen window
499   if (className.EqualsLiteral("ShockwaveFlashFullScreen") ||
500       className.EqualsLiteral("QTNSHIDDEN") ||
501       className.EqualsLiteral("AGFullScreenWinClass")) {
502     SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
503     return true;
504   }
505 
506   // Google Earth bridging msg window between the plugin instance and a separate
507   // earth process. The earth process can trigger a plugin incall on the browser
508   // at any time, which is badness if the instance is already making an incall.
509   if (className.EqualsLiteral("__geplugin_bridge_window__")) {
510     SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
511     return true;
512   }
513 
514   // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the
515   // toolkit app's name. It's pretty expensive to calculate this so we only try
516   // once.
517   if (gAppMessageWindowNameLength == 0) {
518     nsCOMPtr<nsIXULAppInfo> appInfo =
519       do_GetService("@mozilla.org/xre/app-info;1");
520     if (appInfo) {
521       nsAutoCString appName;
522       if (NS_SUCCEEDED(appInfo->GetName(appName))) {
523         appName.AppendLiteral("MessageWindow");
524         nsDependentString windowName(gAppMessageWindowName);
525         CopyUTF8toUTF16(appName, windowName);
526         gAppMessageWindowNameLength = windowName.Length();
527       }
528     }
529 
530     // Don't try again if that failed.
531     if (gAppMessageWindowNameLength == 0) {
532       gAppMessageWindowNameLength = -1;
533     }
534   }
535 
536   if (gAppMessageWindowNameLength != -1 &&
537       className.Equals(nsDependentString(gAppMessageWindowName,
538                                          gAppMessageWindowNameLength))) {
539     return true;
540   }
541 
542   return false;
543 }
544 
545 bool
546 NeuterWindowProcedure(HWND hWnd)
547 {
548   if (!WindowIsDeferredWindow(hWnd)) {
549     // Some other kind of window, skip.
550     return false;
551   }
552 
553   NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!");
554 
555   // It's possible to get nullptr out of SetWindowLongPtr, and the only way to
556   // know if that's a valid old value is to use GetLastError. Clear the error
557   // here so we can tell.
558   SetLastError(ERROR_SUCCESS);
559 
560   LONG_PTR currentWndProc =
561     SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc);
562   if (!currentWndProc) {
563     if (ERROR_SUCCESS == GetLastError()) {
564       // No error, so we set something and must therefore reset it.
565       SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
566     }
567     return false;
568   }
569 
570   NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
571                "This shouldn't be possible!");
572 
573   if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
574     // Cleanup
575     NS_WARNING("SetProp failed!");
576     SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
577     RemovePropW(hWnd, kOldWndProcProp);
578     RemovePropW(hWnd, k3rdPartyWindowProp);
579     return false;
580   }
581 
582   return true;
583 }
584 
585 void
586 RestoreWindowProcedure(HWND hWnd)
587 {
588   NS_ASSERTION(WindowIsDeferredWindow(hWnd),
589                "Not a deferred window, this shouldn't be in our list!");
590   LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp);
591   if (oldWndProc) {
592     NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
593                  "This shouldn't be possible!");
594 
595     DebugOnly<LONG_PTR> currentWndProc =
596       SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
597     NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
598                  "This should never be switched out from under us!");
599   }
600   RemovePropW(hWnd, kOldWndProcProp);
601   RemovePropW(hWnd, k3rdPartyWindowProp);
602 }
603 
604 LRESULT CALLBACK
605 CallWindowProcedureHook(int nCode,
606                         WPARAM wParam,
607                         LPARAM lParam)
608 {
609   if (nCode >= 0) {
610     NS_ASSERTION(gNeuteredWindows, "This should never be null!");
611 
612     HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
613 
614     if (!gNeuteredWindows->Contains(hWnd) &&
615         !SuppressedNeuteringRegion::IsNeuteringSuppressed() &&
616         NeuterWindowProcedure(hWnd)) {
617       if (!gNeuteredWindows->AppendElement(hWnd)) {
618         NS_ERROR("Out of memory!");
619         RestoreWindowProcedure(hWnd);
620       }
621     }
622   }
623   return CallNextHookEx(nullptr, nCode, wParam, lParam);
624 }
625 
626 inline void
627 AssertWindowIsNotNeutered(HWND hWnd)
628 {
629 #ifdef DEBUG
630   // Make sure our neutered window hook isn't still in place.
631   LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
632   NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!");
633 #endif
634 }
635 
636 void
637 UnhookNeuteredWindows()
638 {
639   if (!gNeuteredWindows)
640     return;
641   uint32_t count = gNeuteredWindows->Length();
642   for (uint32_t index = 0; index < count; index++) {
643     RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
644   }
645   gNeuteredWindows->Clear();
646 }
647 
648 // This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
649 // value for GetTickCount(), which is something like 50 days). It uses the
650 // cheapest (and least accurate) method supported by Windows 2000.
651 
652 struct TimeoutData
653 {
654   DWORD startTicks;
655   DWORD targetTicks;
656 };
657 
658 void
659 InitTimeoutData(TimeoutData* aData,
660                 int32_t aTimeoutMs)
661 {
662   aData->startTicks = GetTickCount();
663   if (!aData->startTicks) {
664     // How unlikely is this!
665     aData->startTicks++;
666   }
667   aData->targetTicks = aData->startTicks + aTimeoutMs;
668 }
669 
670 
671 bool
672 TimeoutHasExpired(const TimeoutData& aData)
673 {
674   if (!aData.startTicks) {
675     return false;
676   }
677 
678   DWORD now = GetTickCount();
679 
680   if (aData.targetTicks < aData.startTicks) {
681     // Overflow
682     return now < aData.startTicks && now >= aData.targetTicks;
683   }
684   return now >= aData.targetTicks;
685 }
686 
687 } // namespace
688 
689 namespace mozilla {
690 namespace ipc {
691 namespace windows {
692 
693 void
694 InitUIThread()
695 {
696   // If we aren't setup before a call to NotifyWorkerThread, we'll hang
697   // on startup.
698   if (!gUIThreadId) {
699     gUIThreadId = GetCurrentThreadId();
700   }
701 
702   MOZ_ASSERT(gUIThreadId);
703   MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
704              "Called InitUIThread multiple times on different threads!");
705 
706   if (!gWinEventHook) {
707     gWinEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
708                                     NULL, &WinEventHook, GetCurrentProcessId(),
709                                     gUIThreadId, WINEVENT_OUTOFCONTEXT);
710 
711     // We need to execute this after setting the hook in case the OLE window
712     // already existed.
713     gCOMWindow = FindCOMWindow();
714   }
715   MOZ_ASSERT(gWinEventHook);
716 }
717 
718 } // namespace windows
719 } // namespace ipc
720 } // namespace mozilla
721 
722 // See SpinInternalEventLoop below
723 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
724   : mInterrupt(interrupt)
725   , mSpinNestedEvents(false)
726   , mListenerNotified(false)
727   , mChannel(channel)
728   , mPrev(mChannel->mTopFrame)
729   , mStaticPrev(sStaticTopFrame)
730 {
731   // Only track stack frames when Windows message deferral behavior
732   // is request for the channel.
733   if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
734     return;
735   }
736 
737   mChannel->mTopFrame = this;
738   sStaticTopFrame = this;
739 
740   if (!mStaticPrev) {
741     NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
742     gNeuteredWindows = new AutoTArray<HWND, 20>();
743     NS_ASSERTION(gNeuteredWindows, "Out of memory!");
744   }
745 }
746 
747 MessageChannel::SyncStackFrame::~SyncStackFrame()
748 {
749   if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
750     return;
751   }
752 
753   NS_ASSERTION(this == mChannel->mTopFrame,
754                "Mismatched interrupt stack frames");
755   NS_ASSERTION(this == sStaticTopFrame,
756                "Mismatched static Interrupt stack frames");
757 
758   mChannel->mTopFrame = mPrev;
759   sStaticTopFrame = mStaticPrev;
760 
761   if (!mStaticPrev) {
762     NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
763     delete gNeuteredWindows;
764     gNeuteredWindows = nullptr;
765   }
766 }
767 
768 MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
769 
770 // nsAppShell's notification that gecko events are being processed.
771 // If we are here and there is an Interrupt Incall active, we are spinning
772 // a nested gecko event loop. In which case the remote process needs
773 // to know about it.
774 void /* static */
775 MessageChannel::NotifyGeckoEventDispatch()
776 {
777   // sStaticTopFrame is only valid for Interrupt channels
778   if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
779     return;
780 
781   sStaticTopFrame->mListenerNotified = true;
782   MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
783   channel->Listener()->ProcessRemoteNativeEventsInInterruptCall();
784 }
785 
786 // invoked by the module that receives the spin event loop
787 // message.
788 void
789 MessageChannel::ProcessNativeEventsInInterruptCall()
790 {
791   NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
792                "Shouldn't be on a non-main thread in here!");
793   if (!mTopFrame) {
794     NS_ERROR("Spin logic error: no Interrupt frame");
795     return;
796   }
797 
798   mTopFrame->mSpinNestedEvents = true;
799 }
800 
801 // Spin loop is called in place of WaitFor*Notify when modal ui is being shown
802 // in a child. There are some intricacies in using it however. Spin loop is
803 // enabled for a particular Interrupt frame by the client calling
804 // MessageChannel::ProcessNativeEventsInInterrupt().
805 // This call can be nested for multiple Interrupt frames in a single plugin or
806 // multiple unrelated plugins.
807 void
808 MessageChannel::SpinInternalEventLoop()
809 {
810   if (mozilla::PaintTracker::IsPainting()) {
811     NS_RUNTIMEABORT("Don't spin an event loop while painting.");
812   }
813 
814   NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents,
815                "Spinning incorrectly");
816 
817   // Nested windows event loop we trigger when the child enters into modal
818   // event loops.
819 
820   // Note, when we return, we always reset the notify worker event. So there's
821   // no need to reset it on return here.
822 
823   do {
824     MSG msg = { 0 };
825 
826     // Don't get wrapped up in here if the child connection dies.
827     {
828       MonitorAutoLock lock(*mMonitor);
829       if (!Connected()) {
830         return;
831       }
832     }
833 
834     // Retrieve window or thread messages
835     if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
836       // The child UI should have been destroyed before the app is closed, in
837       // which case, we should never get this here.
838       if (msg.message == WM_QUIT) {
839           NS_ERROR("WM_QUIT received in SpinInternalEventLoop!");
840       } else {
841           TranslateMessage(&msg);
842           ::DispatchMessageW(&msg);
843           return;
844       }
845     }
846 
847     // Note, give dispatching windows events priority over checking if
848     // mEvent is signaled, otherwise heavy ipc traffic can cause jittery
849     // playback of video. We'll exit out on each disaptch above, so ipc
850     // won't get starved.
851 
852     // Wait for UI events or a signal from the io thread.
853     DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
854                                              QS_ALLINPUT);
855     if (result == WAIT_OBJECT_0) {
856       // Our NotifyWorkerThread event was signaled
857       return;
858     }
859   } while (true);
860 }
861 
862 static inline bool
863 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
864 {
865   return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
866     (aTimeout <= (PR_IntervalNow() - aStart));
867 }
868 
869 static HHOOK gWindowHook;
870 
871 static inline void
872 StartNeutering()
873 {
874   MOZ_ASSERT(gUIThreadId);
875   MOZ_ASSERT(!gWindowHook);
876   NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
877                "Shouldn't be pumping already!");
878   MessageChannel::SetIsPumpingMessages(true);
879   gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
880                                    nullptr, gUIThreadId);
881   NS_ASSERTION(gWindowHook, "Failed to set hook!");
882 }
883 
884 static void
885 StopNeutering()
886 {
887   MOZ_ASSERT(MessageChannel::IsPumpingMessages());
888   ::UnhookWindowsHookEx(gWindowHook);
889   gWindowHook = NULL;
890   ::UnhookNeuteredWindows();
891   // Before returning we need to set a hook to run any deferred messages that
892   // we received during the IPC call. The hook will unset itself as soon as
893   // someone else calls GetMessage, PeekMessage, or runs code that generates
894   // a "nonqueued" message.
895   ::ScheduleDeferredMessageRun();
896   MessageChannel::SetIsPumpingMessages(false);
897 }
898 
899 NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
900   : mNeuteredByThis(!gWindowHook && aDoNeuter)
901 {
902   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
903   if (mNeuteredByThis) {
904     StartNeutering();
905   }
906 }
907 
908 NeuteredWindowRegion::~NeuteredWindowRegion()
909 {
910   if (gWindowHook && mNeuteredByThis) {
911     StopNeutering();
912   }
913 }
914 
915 void
916 NeuteredWindowRegion::PumpOnce()
917 {
918   if (!gWindowHook) {
919     // This should be a no-op if nothing has been neutered.
920     return;
921   }
922 
923   MSG msg = {0};
924   // Pump any COM messages so that we don't hang due to STA marshaling.
925   if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
926       ::TranslateMessage(&msg);
927       ::DispatchMessageW(&msg);
928   }
929   // Expunge any nonqueued messages on the current thread.
930   ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
931 }
932 
933 DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
934   : mReneuter(gWindowHook != NULL)
935 {
936   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
937   if (mReneuter) {
938     StopNeutering();
939   }
940 }
941 
942 DeneuteredWindowRegion::~DeneuteredWindowRegion()
943 {
944   if (mReneuter) {
945     StartNeutering();
946   }
947 }
948 
949 SuppressedNeuteringRegion::SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
950   : mReenable(::gUIThreadId == ::GetCurrentThreadId() && ::gWindowHook)
951 {
952   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
953   if (mReenable) {
954     MOZ_ASSERT(!sSuppressNeutering);
955     sSuppressNeutering = true;
956   }
957 }
958 
959 SuppressedNeuteringRegion::~SuppressedNeuteringRegion()
960 {
961   if (mReenable) {
962     MOZ_ASSERT(sSuppressNeutering);
963     sSuppressNeutering = false;
964   }
965 }
966 
967 bool SuppressedNeuteringRegion::sSuppressNeutering = false;
968 
969 #if defined(ACCESSIBILITY)
970 bool
971 MessageChannel::WaitForSyncNotifyWithA11yReentry()
972 {
973   mMonitor->AssertCurrentThreadOwns();
974   MonitorAutoUnlock unlock(*mMonitor);
975 
976   const DWORD waitStart = ::GetTickCount();
977   DWORD elapsed = 0;
978   DWORD timeout = mTimeoutMs == kNoTimeout ? INFINITE :
979                   static_cast<DWORD>(mTimeoutMs);
980   bool timedOut = false;
981 
982   while (true) {
983     { // Scope for lock
984       MonitorAutoLock lock(*mMonitor);
985       if (!Connected()) {
986         break;
987       }
988     }
989     if (timeout != static_cast<DWORD>(kNoTimeout)) {
990       elapsed = ::GetTickCount() - waitStart;
991     }
992     if (elapsed >= timeout) {
993       timedOut = true;
994       break;
995     }
996     DWORD waitResult = 0;
997     ::SetLastError(ERROR_SUCCESS);
998     HRESULT hr = ::CoWaitForMultipleHandles(COWAIT_ALERTABLE,
999                                             timeout - elapsed,
1000                                             1, &mEvent, &waitResult);
1001     if (hr == RPC_S_CALLPENDING) {
1002       timedOut = true;
1003       break;
1004     }
1005     if (hr == S_OK) {
1006       if (waitResult == 0) {
1007         // mEvent is signaled
1008         BOOL success = ::ResetEvent(mEvent);
1009         if (!success) {
1010           gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
1011                       "WindowsMessageChannel::WaitForSyncNotifyWithA11yReentry failed to reset event. GetLastError: " <<
1012                       GetLastError();
1013         }
1014         break;
1015       }
1016       if (waitResult == WAIT_IO_COMPLETION) {
1017         // APC fired, keep waiting
1018         continue;
1019       }
1020     }
1021     NS_ERROR("CoWaitForMultipleHandles failed");
1022     break;
1023   }
1024 
1025   return WaitResponse(timedOut);
1026 }
1027 #endif
1028 
1029 bool
1030 MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
1031 {
1032   mMonitor->AssertCurrentThreadOwns();
1033 
1034   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
1035 
1036 #if defined(ACCESSIBILITY)
1037   if (IsVistaOrLater() && (mFlags & REQUIRE_A11Y_REENTRY)) {
1038     MOZ_ASSERT(!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION));
1039     return WaitForSyncNotifyWithA11yReentry();
1040   }
1041 #endif
1042 
1043   // Use a blocking wait if this channel does not require
1044   // Windows message deferral behavior.
1045   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
1046     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
1047                              PR_INTERVAL_NO_TIMEOUT :
1048                              PR_MillisecondsToInterval(mTimeoutMs);
1049     PRIntervalTime waitStart = 0;
1050 
1051     if (timeout != PR_INTERVAL_NO_TIMEOUT) {
1052       waitStart = PR_IntervalNow();
1053     }
1054 
1055     MOZ_ASSERT(!mIsSyncWaitingOnNonMainThread);
1056     mIsSyncWaitingOnNonMainThread = true;
1057 
1058     mMonitor->Wait(timeout);
1059 
1060     MOZ_ASSERT(mIsSyncWaitingOnNonMainThread);
1061     mIsSyncWaitingOnNonMainThread = false;
1062 
1063     // If the timeout didn't expire, we know we received an event. The
1064     // converse is not true.
1065     return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ?
1066                         false : IsTimeoutExpired(waitStart, timeout));
1067   }
1068 
1069   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
1070                "Shouldn't be here for channels that don't use message deferral!");
1071   NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
1072                "Top frame is not a sync frame!");
1073 
1074   MonitorAutoUnlock unlock(*mMonitor);
1075 
1076   bool timedout = false;
1077 
1078   UINT_PTR timerId = 0;
1079   TimeoutData timeoutData = { 0 };
1080 
1081   if (mTimeoutMs != kNoTimeout) {
1082     InitTimeoutData(&timeoutData, mTimeoutMs);
1083 
1084     // We only do this to ensure that we won't get stuck in
1085     // MsgWaitForMultipleObjects below.
1086     timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
1087     NS_ASSERTION(timerId, "SetTimer failed!");
1088   }
1089 
1090   NeuteredWindowRegion neuteredRgn(true);
1091 
1092   {
1093     while (1) {
1094       MSG msg = { 0 };
1095       // Don't get wrapped up in here if the child connection dies.
1096       {
1097         MonitorAutoLock lock(*mMonitor);
1098         if (!Connected()) {
1099           break;
1100         }
1101       }
1102 
1103       // Wait until we have a message in the queue. MSDN docs are a bit unclear
1104       // but it seems that windows from two different threads (and it should be
1105       // noted that a thread in another process counts as a "different thread")
1106       // will implicitly have their message queues attached if they are parented
1107       // to one another. This wait call, then, will return for a message
1108       // delivered to *either* thread.
1109       DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
1110                                                QS_ALLINPUT);
1111       if (result == WAIT_OBJECT_0) {
1112         // Our NotifyWorkerThread event was signaled
1113         BOOL success = ResetEvent(mEvent);
1114         if (!success) {
1115           gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
1116                       "WindowsMessageChannel::WaitForSyncNotify failed to reset event. GetLastError: " <<
1117                       GetLastError();
1118         }
1119         break;
1120       } else
1121       if (result != (WAIT_OBJECT_0 + 1)) {
1122         NS_ERROR("Wait failed!");
1123         break;
1124       }
1125 
1126       if (TimeoutHasExpired(timeoutData)) {
1127         // A timeout was specified and we've passed it. Break out.
1128         timedout = true;
1129         break;
1130       }
1131 
1132       // The only way to know on which thread the message was delivered is to
1133       // use some logic on the return values of GetQueueStatus and PeekMessage.
1134       // PeekMessage will return false if there are no "queued" messages, but it
1135       // will run all "nonqueued" messages before returning. So if PeekMessage
1136       // returns false and there are no "nonqueued" messages that were run then
1137       // we know that the message we woke for was intended for a window on
1138       // another thread.
1139       bool haveSentMessagesPending =
1140         (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
1141 
1142       // Either of the PeekMessage calls below will actually process all
1143       // "nonqueued" messages that are pending before returning. If we have
1144       // "nonqueued" messages pending then we should have switched out all the
1145       // window procedures above. In that case this PeekMessage call won't
1146       // actually cause any mozilla code (or plugin code) to run.
1147 
1148       // We have to manually pump all COM messages *after* looking at the queue
1149       // queue status but before yielding our thread below.
1150       if (gCOMWindow) {
1151         if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
1152           TranslateMessage(&msg);
1153           ::DispatchMessageW(&msg);
1154         }
1155       }
1156 
1157       // If the following PeekMessage call fails to return a message for us (and
1158       // returns false) and we didn't run any "nonqueued" messages then we must
1159       // have woken up for a message designated for a window in another thread.
1160       // If we loop immediately then we could enter a tight loop, so we'll give
1161       // up our time slice here to let the child process its message.
1162       if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
1163           !haveSentMessagesPending) {
1164         // Message was for child, we should wait a bit.
1165         SwitchToThread();
1166       }
1167     }
1168   }
1169 
1170   if (timerId) {
1171     KillTimer(nullptr, timerId);
1172     timerId = 0;
1173   }
1174 
1175   return WaitResponse(timedout);
1176 }
1177 
1178 bool
1179 MessageChannel::WaitForInterruptNotify()
1180 {
1181   mMonitor->AssertCurrentThreadOwns();
1182 
1183   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
1184 
1185   // Re-use sync notification wait code if this channel does not require
1186   // Windows message deferral behavior.
1187   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
1188     return WaitForSyncNotify(true);
1189   }
1190 
1191   if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
1192     // There is currently no way to recover from this condition.
1193     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
1194   }
1195 
1196   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
1197                "Shouldn't be here for channels that don't use message deferral!");
1198   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
1199                "Top frame is not a sync frame!");
1200 
1201   MonitorAutoUnlock unlock(*mMonitor);
1202 
1203   bool timedout = false;
1204 
1205   UINT_PTR timerId = 0;
1206   TimeoutData timeoutData = { 0 };
1207 
1208   // gWindowHook is used as a flag variable for the loop below: if it is set
1209   // and we start to spin a nested event loop, we need to clear the hook and
1210   // process deferred/pending messages.
1211   while (1) {
1212     NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(),
1213                  "gWindowHook out of sync with reality");
1214 
1215     if (mTopFrame->mSpinNestedEvents) {
1216       if (gWindowHook && timerId) {
1217         KillTimer(nullptr, timerId);
1218         timerId = 0;
1219       }
1220       DeneuteredWindowRegion deneuteredRgn;
1221       SpinInternalEventLoop();
1222       BOOL success = ResetEvent(mEvent);
1223       if (!success) {
1224         gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
1225                     "WindowsMessageChannel::WaitForInterruptNotify::SpinNestedEvents failed to reset event. GetLastError: " <<
1226                     GetLastError();
1227       }
1228       return true;
1229     }
1230 
1231     if (mTimeoutMs != kNoTimeout && !timerId) {
1232       InitTimeoutData(&timeoutData, mTimeoutMs);
1233       timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
1234       NS_ASSERTION(timerId, "SetTimer failed!");
1235     }
1236 
1237     NeuteredWindowRegion neuteredRgn(true);
1238 
1239     MSG msg = { 0 };
1240 
1241     // Don't get wrapped up in here if the child connection dies.
1242     {
1243       MonitorAutoLock lock(*mMonitor);
1244       if (!Connected()) {
1245         break;
1246       }
1247     }
1248 
1249     DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
1250                                              QS_ALLINPUT);
1251     if (result == WAIT_OBJECT_0) {
1252       // Our NotifyWorkerThread event was signaled
1253       BOOL success = ResetEvent(mEvent);
1254       if (!success) {
1255         gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
1256                     "WindowsMessageChannel::WaitForInterruptNotify::WaitForMultipleObjects failed to reset event. GetLastError: " <<
1257                     GetLastError();
1258       }
1259       break;
1260     } else
1261     if (result != (WAIT_OBJECT_0 + 1)) {
1262       NS_ERROR("Wait failed!");
1263       break;
1264     }
1265 
1266     if (TimeoutHasExpired(timeoutData)) {
1267       // A timeout was specified and we've passed it. Break out.
1268       timedout = true;
1269       break;
1270     }
1271 
1272     // See MessageChannel's WaitFor*Notify for details.
1273     bool haveSentMessagesPending =
1274       (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
1275 
1276     // Run all COM messages *after* looking at the queue status.
1277     if (gCOMWindow) {
1278         if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
1279             TranslateMessage(&msg);
1280             ::DispatchMessageW(&msg);
1281         }
1282     }
1283 
1284     // PeekMessage markes the messages as "old" so that they don't wake up
1285     // MsgWaitForMultipleObjects every time.
1286     if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
1287         !haveSentMessagesPending) {
1288       // Message was for child, we should wait a bit.
1289       SwitchToThread();
1290     }
1291   }
1292 
1293   if (timerId) {
1294     KillTimer(nullptr, timerId);
1295     timerId = 0;
1296   }
1297 
1298   return WaitResponse(timedout);
1299 }
1300 
1301 void
1302 MessageChannel::NotifyWorkerThread()
1303 {
1304   mMonitor->AssertCurrentThreadOwns();
1305 
1306   if (mIsSyncWaitingOnNonMainThread) {
1307     mMonitor->Notify();
1308     return;
1309   }
1310 
1311   MOZ_RELEASE_ASSERT(mEvent, "No signal event to set, this is really bad!");
1312   if (!SetEvent(mEvent)) {
1313     NS_WARNING("Failed to set NotifyWorkerThread event!");
1314     gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
1315                 "WindowsMessageChannel failed to SetEvent. GetLastError: " <<
1316                 GetLastError();
1317   }
1318 }
1319 
1320 void
1321 DeferredSendMessage::Run()
1322 {
1323   AssertWindowIsNotNeutered(hWnd);
1324   if (!IsWindow(hWnd)) {
1325     NS_ERROR("Invalid window!");
1326     return;
1327   }
1328 
1329   WNDPROC wndproc =
1330     reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
1331   if (!wndproc) {
1332     NS_ERROR("Invalid window procedure!");
1333     return;
1334   }
1335 
1336   CallWindowProc(wndproc, hWnd, message, wParam, lParam);
1337 }
1338 
1339 void
1340 DeferredRedrawMessage::Run()
1341 {
1342   AssertWindowIsNotNeutered(hWnd);
1343   if (!IsWindow(hWnd)) {
1344     NS_ERROR("Invalid window!");
1345     return;
1346   }
1347 
1348 #ifdef DEBUG
1349   BOOL ret =
1350 #endif
1351   RedrawWindow(hWnd, nullptr, nullptr, flags);
1352   NS_ASSERTION(ret, "RedrawWindow failed!");
1353 }
1354 
1355 DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd)
1356 {
1357   mWnd = aHWnd;
1358   if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) {
1359     memset(&mUpdateRect, 0, sizeof(RECT));
1360     return;
1361   }
1362   ValidateRect(mWnd, &mUpdateRect);
1363 }
1364 
1365 void
1366 DeferredUpdateMessage::Run()
1367 {
1368   AssertWindowIsNotNeutered(mWnd);
1369   if (!IsWindow(mWnd)) {
1370     NS_ERROR("Invalid window!");
1371     return;
1372   }
1373 
1374   InvalidateRect(mWnd, &mUpdateRect, FALSE);
1375 #ifdef DEBUG
1376   BOOL ret =
1377 #endif
1378   UpdateWindow(mWnd);
1379   NS_ASSERTION(ret, "UpdateWindow failed!");
1380 }
1381 
1382 DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd,
1383                                                            UINT aMessage,
1384                                                            WPARAM aWParam,
1385                                                            LPARAM aLParam)
1386 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1387 {
1388   NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!");
1389   if (aLParam) {
1390     lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam));
1391     lParam = reinterpret_cast<LPARAM>(lParamString);
1392   }
1393   else {
1394     lParamString = nullptr;
1395     lParam = 0;
1396   }
1397 }
1398 
1399 DeferredSettingChangeMessage::~DeferredSettingChangeMessage()
1400 {
1401   free(lParamString);
1402 }
1403 
1404 DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd,
1405                                                    LPARAM aLParam,
1406                                                    bool aForCalcSize,
1407                                                    WPARAM aWParam)
1408 {
1409   if (aForCalcSize) {
1410     if (aWParam) {
1411       NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam);
1412       memcpy(&windowPos, arg->lppos, sizeof(windowPos));
1413 
1414       NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
1415     }
1416     else {
1417       RECT* arg = reinterpret_cast<RECT*>(aLParam);
1418       windowPos.hwnd = aHWnd;
1419       windowPos.hwndInsertAfter = nullptr;
1420       windowPos.x = arg->left;
1421       windowPos.y = arg->top;
1422       windowPos.cx = arg->right - arg->left;
1423       windowPos.cy = arg->bottom - arg->top;
1424 
1425       NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top,
1426                    "Negative width or height!");
1427     }
1428     windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1429                       SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING;
1430   }
1431   else {
1432     // Not for WM_NCCALCSIZE
1433     WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam);
1434     memcpy(&windowPos, arg, sizeof(windowPos));
1435 
1436     NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
1437 
1438     // Windows sends in some private flags sometimes that we can't simply copy.
1439     // Filter here.
1440     UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME |
1441                 SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE |
1442                 SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
1443                 SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE |
1444                 SWP_NOZORDER | SWP_SHOWWINDOW;
1445     windowPos.flags &= mask;
1446   }
1447 }
1448 
1449 void
1450 DeferredWindowPosMessage::Run()
1451 {
1452   AssertWindowIsNotNeutered(windowPos.hwnd);
1453   if (!IsWindow(windowPos.hwnd)) {
1454     NS_ERROR("Invalid window!");
1455     return;
1456   }
1457 
1458   if (!IsWindow(windowPos.hwndInsertAfter)) {
1459     NS_WARNING("ZOrder change cannot be honored");
1460     windowPos.hwndInsertAfter = 0;
1461     windowPos.flags |= SWP_NOZORDER;
1462   }
1463 
1464 #ifdef DEBUG
1465   BOOL ret =
1466 #endif
1467   SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x,
1468                windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags);
1469   NS_ASSERTION(ret, "SetWindowPos failed!");
1470 }
1471 
1472 DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd,
1473                                                  UINT aMessage,
1474                                                  WPARAM aWParam,
1475                                                  LPARAM aLParam)
1476 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1477 {
1478   NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!");
1479 
1480   COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam);
1481   NS_ASSERTION(source, "Should never be null!");
1482 
1483   copyData.dwData = source->dwData;
1484   copyData.cbData = source->cbData;
1485 
1486   if (source->cbData) {
1487     copyData.lpData = malloc(source->cbData);
1488     if (copyData.lpData) {
1489       memcpy(copyData.lpData, source->lpData, source->cbData);
1490     }
1491     else {
1492       NS_ERROR("Out of memory?!");
1493       copyData.cbData = 0;
1494     }
1495   }
1496   else {
1497     copyData.lpData = nullptr;
1498   }
1499 
1500   lParam = reinterpret_cast<LPARAM>(&copyData);
1501 }
1502 
1503 DeferredCopyDataMessage::~DeferredCopyDataMessage()
1504 {
1505   free(copyData.lpData);
1506 }
1507 
1508 DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd,
1509                                                        WPARAM aWParam,
1510                                                        LPARAM aLParam)
1511 : hWnd(aHWnd)
1512 {
1513   index = static_cast<int>(aWParam);
1514   style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew;
1515 }
1516 
1517 void
1518 DeferredStyleChangeMessage::Run()
1519 {
1520   SetWindowLongPtr(hWnd, index, style);
1521 }
1522 
1523 DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd,
1524                                                UINT aMessage,
1525                                                WPARAM aWParam,
1526                                                LPARAM aLParam)
1527 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
1528 {
1529   NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!");
1530 }
1531 
1532 void
1533 DeferredSetIconMessage::Run()
1534 {
1535   AssertWindowIsNotNeutered(hWnd);
1536   if (!IsWindow(hWnd)) {
1537     NS_ERROR("Invalid window!");
1538     return;
1539   }
1540 
1541   WNDPROC wndproc =
1542     reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
1543   if (!wndproc) {
1544     NS_ERROR("Invalid window procedure!");
1545     return;
1546   }
1547 
1548   HICON hOld = reinterpret_cast<HICON>(
1549     CallWindowProc(wndproc, hWnd, message, wParam, lParam));
1550   if (hOld) {
1551     DestroyIcon(hOld);
1552   }
1553 }
1554