1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/glue/WindowsMessageLoop.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1296 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et : 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "mozilla/DebugOnly.h" 1.12 + 1.13 +#include "WindowsMessageLoop.h" 1.14 +#include "MessageChannel.h" 1.15 + 1.16 +#include "nsAutoPtr.h" 1.17 +#include "nsServiceManagerUtils.h" 1.18 +#include "nsString.h" 1.19 +#include "nsIXULAppInfo.h" 1.20 + 1.21 +#include "mozilla/PaintTracker.h" 1.22 + 1.23 +using namespace mozilla; 1.24 +using namespace mozilla::ipc; 1.25 +using namespace mozilla::ipc::windows; 1.26 + 1.27 +/** 1.28 + * The Windows-only code below exists to solve a general problem with deadlocks 1.29 + * that we experience when sending synchronous IPC messages to processes that 1.30 + * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous 1.31 + * messages between parent and child HWNDs in multiple circumstances (e.g. 1.32 + * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled 1.33 + * by different threads or different processes. Thus we can very easily end up 1.34 + * in a deadlock by a call stack like the following: 1.35 + * 1.36 + * Process A: 1.37 + * - CreateWindow(...) creates a "parent" HWND. 1.38 + * - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent" 1.39 + * HWND over to Process B. Process A blocks until a response is received 1.40 + * from Process B. 1.41 + * 1.42 + * Process B: 1.43 + * - RecvCreateWidget(HWND) gets the "parent" HWND from Process A. 1.44 + * - CreateWindow(..., HWND) creates a "child" HWND with the parent from 1.45 + * process A. 1.46 + * - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent 1.47 + * synchronously to Process A. Process B blocks until a response is 1.48 + * received from Process A. Process A, however, is blocked and cannot 1.49 + * process the message. Both processes are deadlocked. 1.50 + * 1.51 + * The example above has a few different workarounds (e.g. setting the 1.52 + * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is 1.53 + * persists. Once two HWNDs are parented we must not block their owning 1.54 + * threads when manipulating either HWND. 1.55 + * 1.56 + * Windows requires any application that hosts native HWNDs to always process 1.57 + * messages or risk deadlock. Given our architecture the only way to meet 1.58 + * Windows' requirement and allow for synchronous IPC messages is to pump a 1.59 + * miniature message loop during a sync IPC call. We avoid processing any 1.60 + * queued messages during the loop (with one exception, see below), but 1.61 + * "nonqueued" messages (see 1.62 + * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the 1.63 + * section "Nonqueued messages") cannot be avoided. Those messages are trapped 1.64 + * in a special window procedure where we can either ignore the message or 1.65 + * process it in some fashion. 1.66 + * 1.67 + * Queued and "non-queued" messages will be processed during Interrupt calls if 1.68 + * modal UI related api calls block an Interrupt in-call in the child. To prevent 1.69 + * windows from freezing, and to allow concurrent processing of critical 1.70 + * events (such as painting), we spin a native event dispatch loop while 1.71 + * these in-calls are blocked. 1.72 + */ 1.73 + 1.74 +#if defined(ACCESSIBILITY) 1.75 +// pulled from accessibility's win utils 1.76 +extern const wchar_t* kPropNameTabContent; 1.77 +#endif 1.78 + 1.79 +// widget related message id constants we need to defer 1.80 +namespace mozilla { 1.81 +namespace widget { 1.82 +extern UINT sAppShellGeckoMsgId; 1.83 +#ifdef MOZ_METRO 1.84 +extern UINT sDefaultBrowserMsgId; 1.85 +#endif 1.86 +} 1.87 +} 1.88 + 1.89 +namespace { 1.90 + 1.91 +const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc"; 1.92 + 1.93 +// This isn't defined before Windows XP. 1.94 +enum { WM_XP_THEMECHANGED = 0x031A }; 1.95 + 1.96 +char16_t gAppMessageWindowName[256] = { 0 }; 1.97 +int32_t gAppMessageWindowNameLength = 0; 1.98 + 1.99 +nsTArray<HWND>* gNeuteredWindows = nullptr; 1.100 + 1.101 +typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray; 1.102 +DeferredMessageArray* gDeferredMessages = nullptr; 1.103 + 1.104 +HHOOK gDeferredGetMsgHook = nullptr; 1.105 +HHOOK gDeferredCallWndProcHook = nullptr; 1.106 + 1.107 +DWORD gUIThreadId = 0; 1.108 + 1.109 +LRESULT CALLBACK 1.110 +DeferredMessageHook(int nCode, 1.111 + WPARAM wParam, 1.112 + LPARAM lParam) 1.113 +{ 1.114 + // XXX This function is called for *both* the WH_CALLWNDPROC hook and the 1.115 + // WH_GETMESSAGE hook, but they have different parameters. We don't 1.116 + // use any of them except nCode which has the same meaning. 1.117 + 1.118 + // Only run deferred messages if all of these conditions are met: 1.119 + // 1. The |nCode| indicates that this hook should do something. 1.120 + // 2. We have deferred messages to run. 1.121 + // 3. We're not being called from the PeekMessage within the WaitFor*Notify 1.122 + // function (indicated with MessageChannel::IsPumpingMessages). We really 1.123 + // only want to run after returning to the main event loop. 1.124 + if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) { 1.125 + NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook, 1.126 + "These hooks must be set if we're being called!"); 1.127 + NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!"); 1.128 + 1.129 + // Unset hooks first, in case we reenter below. 1.130 + UnhookWindowsHookEx(gDeferredGetMsgHook); 1.131 + UnhookWindowsHookEx(gDeferredCallWndProcHook); 1.132 + gDeferredGetMsgHook = 0; 1.133 + gDeferredCallWndProcHook = 0; 1.134 + 1.135 + // Unset the global and make sure we delete it when we're done here. 1.136 + nsAutoPtr<DeferredMessageArray> messages(gDeferredMessages); 1.137 + gDeferredMessages = nullptr; 1.138 + 1.139 + // Run all the deferred messages in order. 1.140 + uint32_t count = messages->Length(); 1.141 + for (uint32_t index = 0; index < count; index++) { 1.142 + messages->ElementAt(index)->Run(); 1.143 + } 1.144 + } 1.145 + 1.146 + // Always call the next hook. 1.147 + return CallNextHookEx(nullptr, nCode, wParam, lParam); 1.148 +} 1.149 + 1.150 +void 1.151 +ScheduleDeferredMessageRun() 1.152 +{ 1.153 + if (gDeferredMessages && 1.154 + !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) { 1.155 + NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!"); 1.156 + 1.157 + gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook, 1.158 + nullptr, gUIThreadId); 1.159 + gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC, 1.160 + DeferredMessageHook, nullptr, 1.161 + gUIThreadId); 1.162 + NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook, 1.163 + "Failed to set hooks!"); 1.164 + } 1.165 +} 1.166 + 1.167 +LRESULT 1.168 +ProcessOrDeferMessage(HWND hwnd, 1.169 + UINT uMsg, 1.170 + WPARAM wParam, 1.171 + LPARAM lParam) 1.172 +{ 1.173 + DeferredMessage* deferred = nullptr; 1.174 + 1.175 + // Most messages ask for 0 to be returned if the message is processed. 1.176 + LRESULT res = 0; 1.177 + 1.178 + switch (uMsg) { 1.179 + // Messages that can be deferred as-is. These must not contain pointers in 1.180 + // their wParam or lParam arguments! 1.181 + case WM_ACTIVATE: 1.182 + case WM_ACTIVATEAPP: 1.183 + case WM_CANCELMODE: 1.184 + case WM_CAPTURECHANGED: 1.185 + case WM_CHILDACTIVATE: 1.186 + case WM_DESTROY: 1.187 + case WM_ENABLE: 1.188 + case WM_IME_NOTIFY: 1.189 + case WM_IME_SETCONTEXT: 1.190 + case WM_KILLFOCUS: 1.191 + case WM_MOUSEWHEEL: 1.192 + case WM_NCDESTROY: 1.193 + case WM_PARENTNOTIFY: 1.194 + case WM_SETFOCUS: 1.195 + case WM_SYSCOMMAND: 1.196 + case WM_DISPLAYCHANGE: 1.197 + case WM_SHOWWINDOW: // Intentional fall-through. 1.198 + case WM_XP_THEMECHANGED: { 1.199 + deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam); 1.200 + break; 1.201 + } 1.202 + 1.203 + case WM_DEVICECHANGE: 1.204 + case WM_POWERBROADCAST: 1.205 + case WM_NCACTIVATE: // Intentional fall-through. 1.206 + case WM_SETCURSOR: { 1.207 + // Friggin unconventional return value... 1.208 + res = TRUE; 1.209 + deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam); 1.210 + break; 1.211 + } 1.212 + 1.213 + case WM_MOUSEACTIVATE: { 1.214 + res = MA_NOACTIVATE; 1.215 + deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam); 1.216 + break; 1.217 + } 1.218 + 1.219 + // These messages need to use the RedrawWindow function to generate the 1.220 + // right kind of message. We can't simply fake them as the MSDN docs say 1.221 + // explicitly that paint messages should not be sent by an application. 1.222 + case WM_ERASEBKGND: { 1.223 + UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT | 1.224 + RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW; 1.225 + deferred = new DeferredRedrawMessage(hwnd, flags); 1.226 + break; 1.227 + } 1.228 + 1.229 + // This message will generate a WM_PAINT message if there are invalid 1.230 + // areas. 1.231 + case WM_PAINT: { 1.232 + deferred = new DeferredUpdateMessage(hwnd); 1.233 + break; 1.234 + } 1.235 + 1.236 + // This message holds a string in its lParam that we must copy. 1.237 + case WM_SETTINGCHANGE: { 1.238 + deferred = new DeferredSettingChangeMessage(hwnd, uMsg, wParam, lParam); 1.239 + break; 1.240 + } 1.241 + 1.242 + // These messages are faked via a call to SetWindowPos. 1.243 + case WM_WINDOWPOSCHANGED: { 1.244 + deferred = new DeferredWindowPosMessage(hwnd, lParam); 1.245 + break; 1.246 + } 1.247 + case WM_NCCALCSIZE: { 1.248 + deferred = new DeferredWindowPosMessage(hwnd, lParam, true, wParam); 1.249 + break; 1.250 + } 1.251 + 1.252 + case WM_COPYDATA: { 1.253 + deferred = new DeferredCopyDataMessage(hwnd, uMsg, wParam, lParam); 1.254 + res = TRUE; 1.255 + break; 1.256 + } 1.257 + 1.258 + case WM_STYLECHANGED: { 1.259 + deferred = new DeferredStyleChangeMessage(hwnd, wParam, lParam); 1.260 + break; 1.261 + } 1.262 + 1.263 + case WM_SETICON: { 1.264 + deferred = new DeferredSetIconMessage(hwnd, uMsg, wParam, lParam); 1.265 + break; 1.266 + } 1.267 + 1.268 + // Messages that are safe to pass to DefWindowProc go here. 1.269 + case WM_ENTERIDLE: 1.270 + case WM_GETICON: 1.271 + case WM_NCPAINT: // (never trap nc paint events) 1.272 + case WM_GETMINMAXINFO: 1.273 + case WM_GETTEXT: 1.274 + case WM_NCHITTEST: 1.275 + case WM_STYLECHANGING: // Intentional fall-through. 1.276 + case WM_WINDOWPOSCHANGING: { 1.277 + return DefWindowProc(hwnd, uMsg, wParam, lParam); 1.278 + } 1.279 + 1.280 + // Just return, prevents DefWindowProc from messaging the window 1.281 + // syncronously with other events, which may be deferred. Prevents 1.282 + // random shutdown of aero composition on the window. 1.283 + case WM_SYNCPAINT: 1.284 + return 0; 1.285 + 1.286 + // This message causes QuickTime to make re-entrant calls. 1.287 + // Simply discarding it doesn't seem to hurt anything. 1.288 + case WM_APP-1: 1.289 + return 0; 1.290 + 1.291 + default: { 1.292 + if (uMsg && uMsg == mozilla::widget::sAppShellGeckoMsgId) { 1.293 + // Widget's registered native event callback 1.294 + deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam); 1.295 +#ifdef MOZ_METRO 1.296 + } else if (uMsg && uMsg == mozilla::widget::sDefaultBrowserMsgId) { 1.297 + // Metro widget's system shutdown message 1.298 + deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam); 1.299 +#endif 1.300 + } else { 1.301 + // Unknown messages only 1.302 +#ifdef DEBUG 1.303 + nsAutoCString log("Received \"nonqueued\" message "); 1.304 + log.AppendInt(uMsg); 1.305 + log.AppendLiteral(" during a synchronous IPC message for window "); 1.306 + log.AppendInt((int64_t)hwnd); 1.307 + 1.308 + wchar_t className[256] = { 0 }; 1.309 + if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) { 1.310 + log.AppendLiteral(" (\""); 1.311 + log.Append(NS_ConvertUTF16toUTF8((char16_t*)className)); 1.312 + log.AppendLiteral("\")"); 1.313 + } 1.314 + 1.315 + log.AppendLiteral(", sending it to DefWindowProc instead of the normal " 1.316 + "window procedure."); 1.317 + NS_ERROR(log.get()); 1.318 +#endif 1.319 + return DefWindowProc(hwnd, uMsg, wParam, lParam); 1.320 + } 1.321 + } 1.322 + } 1.323 + 1.324 + NS_ASSERTION(deferred, "Must have a message here!"); 1.325 + 1.326 + // Create the deferred message array if it doesn't exist already. 1.327 + if (!gDeferredMessages) { 1.328 + gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20); 1.329 + NS_ASSERTION(gDeferredMessages, "Out of memory!"); 1.330 + } 1.331 + 1.332 + // Save for later. The array takes ownership of |deferred|. 1.333 + gDeferredMessages->AppendElement(deferred); 1.334 + return res; 1.335 +} 1.336 + 1.337 +} // anonymous namespace 1.338 + 1.339 +// We need the pointer value of this in PluginInstanceChild. 1.340 +LRESULT CALLBACK 1.341 +NeuteredWindowProc(HWND hwnd, 1.342 + UINT uMsg, 1.343 + WPARAM wParam, 1.344 + LPARAM lParam) 1.345 +{ 1.346 + WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp); 1.347 + if (!oldWndProc) { 1.348 + // We should really never ever get here. 1.349 + NS_ERROR("No old wndproc!"); 1.350 + return DefWindowProc(hwnd, uMsg, wParam, lParam); 1.351 + } 1.352 + 1.353 + // See if we care about this message. We may either ignore it, send it to 1.354 + // DefWindowProc, or defer it for later. 1.355 + return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam); 1.356 +} 1.357 + 1.358 +namespace { 1.359 + 1.360 +static bool 1.361 +WindowIsDeferredWindow(HWND hWnd) 1.362 +{ 1.363 + if (!IsWindow(hWnd)) { 1.364 + NS_WARNING("Window has died!"); 1.365 + return false; 1.366 + } 1.367 + 1.368 + char16_t buffer[256] = { 0 }; 1.369 + int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1); 1.370 + if (length <= 0) { 1.371 + NS_WARNING("Failed to get class name!"); 1.372 + return false; 1.373 + } 1.374 + 1.375 +#if defined(ACCESSIBILITY) 1.376 + // Tab content creates a window that responds to accessible WM_GETOBJECT 1.377 + // calls. This window can safely be ignored. 1.378 + if (::GetPropW(hWnd, kPropNameTabContent)) { 1.379 + return false; 1.380 + } 1.381 +#endif 1.382 + 1.383 + // Common mozilla windows we must defer messages to. 1.384 + nsDependentString className(buffer, length); 1.385 + if (StringBeginsWith(className, NS_LITERAL_STRING("Mozilla")) || 1.386 + StringBeginsWith(className, NS_LITERAL_STRING("Gecko")) || 1.387 + className.EqualsLiteral("nsToolkitClass") || 1.388 + className.EqualsLiteral("nsAppShell:EventWindowClass")) { 1.389 + return true; 1.390 + } 1.391 + 1.392 +#ifdef MOZ_METRO 1.393 + // immersive UI ICoreWindow 1.394 + if (className.EqualsLiteral("Windows.UI.Core.CoreWindow")) { 1.395 + return true; 1.396 + } 1.397 +#endif 1.398 + 1.399 + // Plugin windows that can trigger ipc calls in child: 1.400 + // 'ShockwaveFlashFullScreen' - flash fullscreen window 1.401 + // 'QTNSHIDDEN' - QuickTime 1.402 + // 'AGFullScreenWinClass' - silverlight fullscreen window 1.403 + if (className.EqualsLiteral("ShockwaveFlashFullScreen") || 1.404 + className.EqualsLiteral("QTNSHIDDEN") || 1.405 + className.EqualsLiteral("AGFullScreenWinClass")) { 1.406 + return true; 1.407 + } 1.408 + 1.409 + // Google Earth bridging msg window between the plugin instance and a separate 1.410 + // earth process. The earth process can trigger a plugin incall on the browser 1.411 + // at any time, which is badness if the instance is already making an incall. 1.412 + if (className.EqualsLiteral("__geplugin_bridge_window__")) { 1.413 + return true; 1.414 + } 1.415 + 1.416 + // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the 1.417 + // toolkit app's name. It's pretty expensive to calculate this so we only try 1.418 + // once. 1.419 + if (gAppMessageWindowNameLength == 0) { 1.420 + nsCOMPtr<nsIXULAppInfo> appInfo = 1.421 + do_GetService("@mozilla.org/xre/app-info;1"); 1.422 + if (appInfo) { 1.423 + nsAutoCString appName; 1.424 + if (NS_SUCCEEDED(appInfo->GetName(appName))) { 1.425 + appName.Append("MessageWindow"); 1.426 + nsDependentString windowName(gAppMessageWindowName); 1.427 + CopyUTF8toUTF16(appName, windowName); 1.428 + gAppMessageWindowNameLength = windowName.Length(); 1.429 + } 1.430 + } 1.431 + 1.432 + // Don't try again if that failed. 1.433 + if (gAppMessageWindowNameLength == 0) { 1.434 + gAppMessageWindowNameLength = -1; 1.435 + } 1.436 + } 1.437 + 1.438 + if (gAppMessageWindowNameLength != -1 && 1.439 + className.Equals(nsDependentString(gAppMessageWindowName, 1.440 + gAppMessageWindowNameLength))) { 1.441 + return true; 1.442 + } 1.443 + 1.444 + return false; 1.445 +} 1.446 + 1.447 +bool 1.448 +NeuterWindowProcedure(HWND hWnd) 1.449 +{ 1.450 + if (!WindowIsDeferredWindow(hWnd)) { 1.451 + // Some other kind of window, skip. 1.452 + return false; 1.453 + } 1.454 + 1.455 + NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!"); 1.456 + 1.457 + // It's possible to get nullptr out of SetWindowLongPtr, and the only way to 1.458 + // know if that's a valid old value is to use GetLastError. Clear the error 1.459 + // here so we can tell. 1.460 + SetLastError(ERROR_SUCCESS); 1.461 + 1.462 + LONG_PTR currentWndProc = 1.463 + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc); 1.464 + if (!currentWndProc) { 1.465 + if (ERROR_SUCCESS == GetLastError()) { 1.466 + // No error, so we set something and must therefore reset it. 1.467 + SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc); 1.468 + } 1.469 + return false; 1.470 + } 1.471 + 1.472 + NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc, 1.473 + "This shouldn't be possible!"); 1.474 + 1.475 + if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) { 1.476 + // Cleanup 1.477 + NS_WARNING("SetProp failed!"); 1.478 + SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc); 1.479 + RemoveProp(hWnd, kOldWndProcProp); 1.480 + return false; 1.481 + } 1.482 + 1.483 + return true; 1.484 +} 1.485 + 1.486 +void 1.487 +RestoreWindowProcedure(HWND hWnd) 1.488 +{ 1.489 + NS_ASSERTION(WindowIsDeferredWindow(hWnd), 1.490 + "Not a deferred window, this shouldn't be in our list!"); 1.491 + LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp); 1.492 + if (oldWndProc) { 1.493 + NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc, 1.494 + "This shouldn't be possible!"); 1.495 + 1.496 + DebugOnly<LONG_PTR> currentWndProc = 1.497 + SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc); 1.498 + NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc, 1.499 + "This should never be switched out from under us!"); 1.500 + } 1.501 + RemoveProp(hWnd, kOldWndProcProp); 1.502 +} 1.503 + 1.504 +LRESULT CALLBACK 1.505 +CallWindowProcedureHook(int nCode, 1.506 + WPARAM wParam, 1.507 + LPARAM lParam) 1.508 +{ 1.509 + if (nCode >= 0) { 1.510 + NS_ASSERTION(gNeuteredWindows, "This should never be null!"); 1.511 + 1.512 + HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd; 1.513 + 1.514 + if (!gNeuteredWindows->Contains(hWnd) && NeuterWindowProcedure(hWnd)) { 1.515 + if (!gNeuteredWindows->AppendElement(hWnd)) { 1.516 + NS_ERROR("Out of memory!"); 1.517 + RestoreWindowProcedure(hWnd); 1.518 + } 1.519 + } 1.520 + } 1.521 + return CallNextHookEx(nullptr, nCode, wParam, lParam); 1.522 +} 1.523 + 1.524 +inline void 1.525 +AssertWindowIsNotNeutered(HWND hWnd) 1.526 +{ 1.527 +#ifdef DEBUG 1.528 + // Make sure our neutered window hook isn't still in place. 1.529 + LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC); 1.530 + NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!"); 1.531 +#endif 1.532 +} 1.533 + 1.534 +void 1.535 +UnhookNeuteredWindows() 1.536 +{ 1.537 + if (!gNeuteredWindows) 1.538 + return; 1.539 + uint32_t count = gNeuteredWindows->Length(); 1.540 + for (uint32_t index = 0; index < count; index++) { 1.541 + RestoreWindowProcedure(gNeuteredWindows->ElementAt(index)); 1.542 + } 1.543 + gNeuteredWindows->Clear(); 1.544 +} 1.545 + 1.546 +// This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow 1.547 +// value for GetTickCount(), which is something like 50 days). It uses the 1.548 +// cheapest (and least accurate) method supported by Windows 2000. 1.549 + 1.550 +struct TimeoutData 1.551 +{ 1.552 + DWORD startTicks; 1.553 + DWORD targetTicks; 1.554 +}; 1.555 + 1.556 +void 1.557 +InitTimeoutData(TimeoutData* aData, 1.558 + int32_t aTimeoutMs) 1.559 +{ 1.560 + aData->startTicks = GetTickCount(); 1.561 + if (!aData->startTicks) { 1.562 + // How unlikely is this! 1.563 + aData->startTicks++; 1.564 + } 1.565 + aData->targetTicks = aData->startTicks + aTimeoutMs; 1.566 +} 1.567 + 1.568 + 1.569 +bool 1.570 +TimeoutHasExpired(const TimeoutData& aData) 1.571 +{ 1.572 + if (!aData.startTicks) { 1.573 + return false; 1.574 + } 1.575 + 1.576 + DWORD now = GetTickCount(); 1.577 + 1.578 + if (aData.targetTicks < aData.startTicks) { 1.579 + // Overflow 1.580 + return now < aData.startTicks && now >= aData.targetTicks; 1.581 + } 1.582 + return now >= aData.targetTicks; 1.583 +} 1.584 + 1.585 +} // anonymous namespace 1.586 + 1.587 +namespace mozilla { 1.588 +namespace ipc { 1.589 +namespace windows { 1.590 + 1.591 +void 1.592 +InitUIThread() 1.593 +{ 1.594 + // If we aren't setup before a call to NotifyWorkerThread, we'll hang 1.595 + // on startup. 1.596 + if (!gUIThreadId) { 1.597 + gUIThreadId = GetCurrentThreadId(); 1.598 + } 1.599 + MOZ_ASSERT(gUIThreadId); 1.600 + MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(), 1.601 + "Called InitUIThread multiple times on different threads!"); 1.602 +} 1.603 + 1.604 +} // namespace windows 1.605 +} // namespace ipc 1.606 +} // namespace mozilla 1.607 + 1.608 +// See SpinInternalEventLoop below 1.609 +MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt) 1.610 + : mInterrupt(interrupt) 1.611 + , mSpinNestedEvents(false) 1.612 + , mListenerNotified(false) 1.613 + , mChannel(channel) 1.614 + , mPrev(mChannel->mTopFrame) 1.615 + , mStaticPrev(sStaticTopFrame) 1.616 +{ 1.617 + // Only track stack frames when Windows message deferral behavior 1.618 + // is request for the channel. 1.619 + if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { 1.620 + return; 1.621 + } 1.622 + 1.623 + mChannel->mTopFrame = this; 1.624 + sStaticTopFrame = this; 1.625 + 1.626 + if (!mStaticPrev) { 1.627 + NS_ASSERTION(!gNeuteredWindows, "Should only set this once!"); 1.628 + gNeuteredWindows = new nsAutoTArray<HWND, 20>(); 1.629 + NS_ASSERTION(gNeuteredWindows, "Out of memory!"); 1.630 + } 1.631 +} 1.632 + 1.633 +MessageChannel::SyncStackFrame::~SyncStackFrame() 1.634 +{ 1.635 + if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { 1.636 + return; 1.637 + } 1.638 + 1.639 + NS_ASSERTION(this == mChannel->mTopFrame, 1.640 + "Mismatched interrupt stack frames"); 1.641 + NS_ASSERTION(this == sStaticTopFrame, 1.642 + "Mismatched static Interrupt stack frames"); 1.643 + 1.644 + mChannel->mTopFrame = mPrev; 1.645 + sStaticTopFrame = mStaticPrev; 1.646 + 1.647 + if (!mStaticPrev) { 1.648 + NS_ASSERTION(gNeuteredWindows, "Bad pointer!"); 1.649 + delete gNeuteredWindows; 1.650 + gNeuteredWindows = nullptr; 1.651 + } 1.652 +} 1.653 + 1.654 +MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame; 1.655 + 1.656 +// nsAppShell's notification that gecko events are being processed. 1.657 +// If we are here and there is an Interrupt Incall active, we are spinning 1.658 +// a nested gecko event loop. In which case the remote process needs 1.659 +// to know about it. 1.660 +void /* static */ 1.661 +MessageChannel::NotifyGeckoEventDispatch() 1.662 +{ 1.663 + // sStaticTopFrame is only valid for Interrupt channels 1.664 + if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified) 1.665 + return; 1.666 + 1.667 + sStaticTopFrame->mListenerNotified = true; 1.668 + MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel); 1.669 + channel->Listener()->ProcessRemoteNativeEventsInInterruptCall(); 1.670 +} 1.671 + 1.672 +// invoked by the module that receives the spin event loop 1.673 +// message. 1.674 +void 1.675 +MessageChannel::ProcessNativeEventsInInterruptCall() 1.676 +{ 1.677 + NS_ASSERTION(GetCurrentThreadId() == gUIThreadId, 1.678 + "Shouldn't be on a non-main thread in here!"); 1.679 + if (!mTopFrame) { 1.680 + NS_ERROR("Spin logic error: no Interrupt frame"); 1.681 + return; 1.682 + } 1.683 + 1.684 + mTopFrame->mSpinNestedEvents = true; 1.685 +} 1.686 + 1.687 +// Spin loop is called in place of WaitFor*Notify when modal ui is being shown 1.688 +// in a child. There are some intricacies in using it however. Spin loop is 1.689 +// enabled for a particular Interrupt frame by the client calling 1.690 +// MessageChannel::ProcessNativeEventsInInterrupt(). 1.691 +// This call can be nested for multiple Interrupt frames in a single plugin or 1.692 +// multiple unrelated plugins. 1.693 +void 1.694 +MessageChannel::SpinInternalEventLoop() 1.695 +{ 1.696 + if (mozilla::PaintTracker::IsPainting()) { 1.697 + NS_RUNTIMEABORT("Don't spin an event loop while painting."); 1.698 + } 1.699 + 1.700 + NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents, 1.701 + "Spinning incorrectly"); 1.702 + 1.703 + // Nested windows event loop we trigger when the child enters into modal 1.704 + // event loops. 1.705 + 1.706 + // Note, when we return, we always reset the notify worker event. So there's 1.707 + // no need to reset it on return here. 1.708 + 1.709 + do { 1.710 + MSG msg = { 0 }; 1.711 + 1.712 + // Don't get wrapped up in here if the child connection dies. 1.713 + { 1.714 + MonitorAutoLock lock(*mMonitor); 1.715 + if (!Connected()) { 1.716 + return; 1.717 + } 1.718 + } 1.719 + 1.720 + // Retrieve window or thread messages 1.721 + if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { 1.722 + // The child UI should have been destroyed before the app is closed, in 1.723 + // which case, we should never get this here. 1.724 + if (msg.message == WM_QUIT) { 1.725 + NS_ERROR("WM_QUIT received in SpinInternalEventLoop!"); 1.726 + } else { 1.727 + TranslateMessage(&msg); 1.728 + ::DispatchMessageW(&msg); 1.729 + return; 1.730 + } 1.731 + } 1.732 + 1.733 + // Note, give dispatching windows events priority over checking if 1.734 + // mEvent is signaled, otherwise heavy ipc traffic can cause jittery 1.735 + // playback of video. We'll exit out on each disaptch above, so ipc 1.736 + // won't get starved. 1.737 + 1.738 + // Wait for UI events or a signal from the io thread. 1.739 + DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE, 1.740 + QS_ALLINPUT); 1.741 + if (result == WAIT_OBJECT_0) { 1.742 + // Our NotifyWorkerThread event was signaled 1.743 + return; 1.744 + } 1.745 + } while (true); 1.746 +} 1.747 + 1.748 +static inline bool 1.749 +IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout) 1.750 +{ 1.751 + return (aTimeout != PR_INTERVAL_NO_TIMEOUT) && 1.752 + (aTimeout <= (PR_IntervalNow() - aStart)); 1.753 +} 1.754 + 1.755 +bool 1.756 +MessageChannel::WaitForSyncNotify() 1.757 +{ 1.758 + mMonitor->AssertCurrentThreadOwns(); 1.759 + 1.760 + MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!"); 1.761 + 1.762 + // Use a blocking wait if this channel does not require 1.763 + // Windows message deferral behavior. 1.764 + if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { 1.765 + PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ? 1.766 + PR_INTERVAL_NO_TIMEOUT : 1.767 + PR_MillisecondsToInterval(mTimeoutMs); 1.768 + // XXX could optimize away this syscall for "no timeout" case if desired 1.769 + PRIntervalTime waitStart = 0; 1.770 + 1.771 + if (timeout != PR_INTERVAL_NO_TIMEOUT) { 1.772 + waitStart = PR_IntervalNow(); 1.773 + } 1.774 + 1.775 + mIsSyncWaitingOnNonMainThread = true; 1.776 + 1.777 + mMonitor->Wait(timeout); 1.778 + 1.779 + mIsSyncWaitingOnNonMainThread = false; 1.780 + 1.781 + // If the timeout didn't expire, we know we received an event. The 1.782 + // converse is not true. 1.783 + return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ? 1.784 + false : IsTimeoutExpired(waitStart, timeout)); 1.785 + } 1.786 + 1.787 + NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION, 1.788 + "Shouldn't be here for channels that don't use message deferral!"); 1.789 + NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt, 1.790 + "Top frame is not a sync frame!"); 1.791 + 1.792 + MonitorAutoUnlock unlock(*mMonitor); 1.793 + 1.794 + bool timedout = false; 1.795 + 1.796 + UINT_PTR timerId = 0; 1.797 + TimeoutData timeoutData = { 0 }; 1.798 + 1.799 + if (mTimeoutMs != kNoTimeout) { 1.800 + InitTimeoutData(&timeoutData, mTimeoutMs); 1.801 + 1.802 + // We only do this to ensure that we won't get stuck in 1.803 + // MsgWaitForMultipleObjects below. 1.804 + timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr); 1.805 + NS_ASSERTION(timerId, "SetTimer failed!"); 1.806 + } 1.807 + 1.808 + // Setup deferred processing of native events while we wait for a response. 1.809 + NS_ASSERTION(!MessageChannel::IsPumpingMessages(), 1.810 + "Shouldn't be pumping already!"); 1.811 + 1.812 + MessageChannel::SetIsPumpingMessages(true); 1.813 + HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook, 1.814 + nullptr, gUIThreadId); 1.815 + NS_ASSERTION(windowHook, "Failed to set hook!"); 1.816 + 1.817 + { 1.818 + while (1) { 1.819 + MSG msg = { 0 }; 1.820 + // Don't get wrapped up in here if the child connection dies. 1.821 + { 1.822 + MonitorAutoLock lock(*mMonitor); 1.823 + if (!Connected()) { 1.824 + break; 1.825 + } 1.826 + } 1.827 + 1.828 + // Wait until we have a message in the queue. MSDN docs are a bit unclear 1.829 + // but it seems that windows from two different threads (and it should be 1.830 + // noted that a thread in another process counts as a "different thread") 1.831 + // will implicitly have their message queues attached if they are parented 1.832 + // to one another. This wait call, then, will return for a message 1.833 + // delivered to *either* thread. 1.834 + DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE, 1.835 + QS_ALLINPUT); 1.836 + if (result == WAIT_OBJECT_0) { 1.837 + // Our NotifyWorkerThread event was signaled 1.838 + ResetEvent(mEvent); 1.839 + break; 1.840 + } else 1.841 + if (result != (WAIT_OBJECT_0 + 1)) { 1.842 + NS_ERROR("Wait failed!"); 1.843 + break; 1.844 + } 1.845 + 1.846 + if (TimeoutHasExpired(timeoutData)) { 1.847 + // A timeout was specified and we've passed it. Break out. 1.848 + timedout = true; 1.849 + break; 1.850 + } 1.851 + 1.852 + // The only way to know on which thread the message was delivered is to 1.853 + // use some logic on the return values of GetQueueStatus and PeekMessage. 1.854 + // PeekMessage will return false if there are no "queued" messages, but it 1.855 + // will run all "nonqueued" messages before returning. So if PeekMessage 1.856 + // returns false and there are no "nonqueued" messages that were run then 1.857 + // we know that the message we woke for was intended for a window on 1.858 + // another thread. 1.859 + bool haveSentMessagesPending = 1.860 + (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0; 1.861 + 1.862 + // This PeekMessage call will actually process all "nonqueued" messages 1.863 + // that are pending before returning. If we have "nonqueued" messages 1.864 + // pending then we should have switched out all the window procedures 1.865 + // above. In that case this PeekMessage call won't actually cause any 1.866 + // mozilla code (or plugin code) to run. 1.867 + 1.868 + // If the following PeekMessage call fails to return a message for us (and 1.869 + // returns false) and we didn't run any "nonqueued" messages then we must 1.870 + // have woken up for a message designated for a window in another thread. 1.871 + // If we loop immediately then we could enter a tight loop, so we'll give 1.872 + // up our time slice here to let the child process its message. 1.873 + if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) && 1.874 + !haveSentMessagesPending) { 1.875 + // Message was for child, we should wait a bit. 1.876 + SwitchToThread(); 1.877 + } 1.878 + } 1.879 + } 1.880 + 1.881 + // Unhook the neutered window procedure hook. 1.882 + UnhookWindowsHookEx(windowHook); 1.883 + 1.884 + // Unhook any neutered windows procedures so messages can be delivered 1.885 + // normally. 1.886 + UnhookNeuteredWindows(); 1.887 + 1.888 + // Before returning we need to set a hook to run any deferred messages that 1.889 + // we received during the IPC call. The hook will unset itself as soon as 1.890 + // someone else calls GetMessage, PeekMessage, or runs code that generates 1.891 + // a "nonqueued" message. 1.892 + ScheduleDeferredMessageRun(); 1.893 + 1.894 + if (timerId) { 1.895 + KillTimer(nullptr, timerId); 1.896 + } 1.897 + 1.898 + MessageChannel::SetIsPumpingMessages(false); 1.899 + 1.900 + return WaitResponse(timedout); 1.901 +} 1.902 + 1.903 +bool 1.904 +MessageChannel::WaitForInterruptNotify() 1.905 +{ 1.906 + mMonitor->AssertCurrentThreadOwns(); 1.907 + 1.908 + MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!"); 1.909 + 1.910 + // Re-use sync notification wait code if this channel does not require 1.911 + // Windows message deferral behavior. 1.912 + if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { 1.913 + return WaitForSyncNotify(); 1.914 + } 1.915 + 1.916 + if (!InterruptStackDepth()) { 1.917 + // There is currently no way to recover from this condition. 1.918 + NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!"); 1.919 + } 1.920 + 1.921 + NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION, 1.922 + "Shouldn't be here for channels that don't use message deferral!"); 1.923 + NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt, 1.924 + "Top frame is not a sync frame!"); 1.925 + 1.926 + MonitorAutoUnlock unlock(*mMonitor); 1.927 + 1.928 + bool timedout = false; 1.929 + 1.930 + UINT_PTR timerId = 0; 1.931 + TimeoutData timeoutData = { 0 }; 1.932 + 1.933 + // windowHook is used as a flag variable for the loop below: if it is set 1.934 + // and we start to spin a nested event loop, we need to clear the hook and 1.935 + // process deferred/pending messages. 1.936 + // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false. 1.937 + HHOOK windowHook = nullptr; 1.938 + 1.939 + while (1) { 1.940 + NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(), 1.941 + "windowHook out of sync with reality"); 1.942 + 1.943 + if (mTopFrame->mSpinNestedEvents) { 1.944 + if (windowHook) { 1.945 + UnhookWindowsHookEx(windowHook); 1.946 + windowHook = nullptr; 1.947 + 1.948 + if (timerId) { 1.949 + KillTimer(nullptr, timerId); 1.950 + timerId = 0; 1.951 + } 1.952 + 1.953 + // Used by widget to assert on incoming native events 1.954 + MessageChannel::SetIsPumpingMessages(false); 1.955 + 1.956 + // Unhook any neutered windows procedures so messages can be delievered 1.957 + // normally. 1.958 + UnhookNeuteredWindows(); 1.959 + 1.960 + // Send all deferred "nonqueued" message to the intended receiver. 1.961 + // We're dropping into SpinInternalEventLoop so we should be fairly 1.962 + // certain these will get delivered soohn. 1.963 + ScheduleDeferredMessageRun(); 1.964 + } 1.965 + SpinInternalEventLoop(); 1.966 + ResetEvent(mEvent); 1.967 + return true; 1.968 + } 1.969 + 1.970 + if (!windowHook) { 1.971 + MessageChannel::SetIsPumpingMessages(true); 1.972 + windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook, 1.973 + nullptr, gUIThreadId); 1.974 + NS_ASSERTION(windowHook, "Failed to set hook!"); 1.975 + 1.976 + NS_ASSERTION(!timerId, "Timer already initialized?"); 1.977 + 1.978 + if (mTimeoutMs != kNoTimeout) { 1.979 + InitTimeoutData(&timeoutData, mTimeoutMs); 1.980 + timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr); 1.981 + NS_ASSERTION(timerId, "SetTimer failed!"); 1.982 + } 1.983 + } 1.984 + 1.985 + MSG msg = { 0 }; 1.986 + 1.987 + // Don't get wrapped up in here if the child connection dies. 1.988 + { 1.989 + MonitorAutoLock lock(*mMonitor); 1.990 + if (!Connected()) { 1.991 + break; 1.992 + } 1.993 + } 1.994 + 1.995 + DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE, 1.996 + QS_ALLINPUT); 1.997 + if (result == WAIT_OBJECT_0) { 1.998 + // Our NotifyWorkerThread event was signaled 1.999 + ResetEvent(mEvent); 1.1000 + break; 1.1001 + } else 1.1002 + if (result != (WAIT_OBJECT_0 + 1)) { 1.1003 + NS_ERROR("Wait failed!"); 1.1004 + break; 1.1005 + } 1.1006 + 1.1007 + if (TimeoutHasExpired(timeoutData)) { 1.1008 + // A timeout was specified and we've passed it. Break out. 1.1009 + timedout = true; 1.1010 + break; 1.1011 + } 1.1012 + 1.1013 + // See MessageChannel's WaitFor*Notify for details. 1.1014 + bool haveSentMessagesPending = 1.1015 + (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0; 1.1016 + 1.1017 + // PeekMessage markes the messages as "old" so that they don't wake up 1.1018 + // MsgWaitForMultipleObjects every time. 1.1019 + if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) && 1.1020 + !haveSentMessagesPending) { 1.1021 + // Message was for child, we should wait a bit. 1.1022 + SwitchToThread(); 1.1023 + } 1.1024 + } 1.1025 + 1.1026 + if (windowHook) { 1.1027 + // Unhook the neutered window procedure hook. 1.1028 + UnhookWindowsHookEx(windowHook); 1.1029 + 1.1030 + // Unhook any neutered windows procedures so messages can be delivered 1.1031 + // normally. 1.1032 + UnhookNeuteredWindows(); 1.1033 + 1.1034 + // Before returning we need to set a hook to run any deferred messages that 1.1035 + // we received during the IPC call. The hook will unset itself as soon as 1.1036 + // someone else calls GetMessage, PeekMessage, or runs code that generates 1.1037 + // a "nonqueued" message. 1.1038 + ScheduleDeferredMessageRun(); 1.1039 + 1.1040 + if (timerId) { 1.1041 + KillTimer(nullptr, timerId); 1.1042 + } 1.1043 + } 1.1044 + 1.1045 + MessageChannel::SetIsPumpingMessages(false); 1.1046 + 1.1047 + return WaitResponse(timedout); 1.1048 +} 1.1049 + 1.1050 +void 1.1051 +MessageChannel::NotifyWorkerThread() 1.1052 +{ 1.1053 + mMonitor->AssertCurrentThreadOwns(); 1.1054 + 1.1055 + if (mIsSyncWaitingOnNonMainThread) { 1.1056 + mMonitor->Notify(); 1.1057 + return; 1.1058 + } 1.1059 + 1.1060 + NS_ASSERTION(mEvent, "No signal event to set, this is really bad!"); 1.1061 + if (!SetEvent(mEvent)) { 1.1062 + NS_WARNING("Failed to set NotifyWorkerThread event!"); 1.1063 + } 1.1064 +} 1.1065 + 1.1066 +void 1.1067 +DeferredSendMessage::Run() 1.1068 +{ 1.1069 + AssertWindowIsNotNeutered(hWnd); 1.1070 + if (!IsWindow(hWnd)) { 1.1071 + NS_ERROR("Invalid window!"); 1.1072 + return; 1.1073 + } 1.1074 + 1.1075 + WNDPROC wndproc = 1.1076 + reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC)); 1.1077 + if (!wndproc) { 1.1078 + NS_ERROR("Invalid window procedure!"); 1.1079 + return; 1.1080 + } 1.1081 + 1.1082 + CallWindowProc(wndproc, hWnd, message, wParam, lParam); 1.1083 +} 1.1084 + 1.1085 +void 1.1086 +DeferredRedrawMessage::Run() 1.1087 +{ 1.1088 + AssertWindowIsNotNeutered(hWnd); 1.1089 + if (!IsWindow(hWnd)) { 1.1090 + NS_ERROR("Invalid window!"); 1.1091 + return; 1.1092 + } 1.1093 + 1.1094 +#ifdef DEBUG 1.1095 + BOOL ret = 1.1096 +#endif 1.1097 + RedrawWindow(hWnd, nullptr, nullptr, flags); 1.1098 + NS_ASSERTION(ret, "RedrawWindow failed!"); 1.1099 +} 1.1100 + 1.1101 +DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd) 1.1102 +{ 1.1103 + mWnd = aHWnd; 1.1104 + if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) { 1.1105 + memset(&mUpdateRect, 0, sizeof(RECT)); 1.1106 + return; 1.1107 + } 1.1108 + ValidateRect(mWnd, &mUpdateRect); 1.1109 +} 1.1110 + 1.1111 +void 1.1112 +DeferredUpdateMessage::Run() 1.1113 +{ 1.1114 + AssertWindowIsNotNeutered(mWnd); 1.1115 + if (!IsWindow(mWnd)) { 1.1116 + NS_ERROR("Invalid window!"); 1.1117 + return; 1.1118 + } 1.1119 + 1.1120 + InvalidateRect(mWnd, &mUpdateRect, FALSE); 1.1121 +#ifdef DEBUG 1.1122 + BOOL ret = 1.1123 +#endif 1.1124 + UpdateWindow(mWnd); 1.1125 + NS_ASSERTION(ret, "UpdateWindow failed!"); 1.1126 +} 1.1127 + 1.1128 +DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd, 1.1129 + UINT aMessage, 1.1130 + WPARAM aWParam, 1.1131 + LPARAM aLParam) 1.1132 +: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) 1.1133 +{ 1.1134 + NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!"); 1.1135 + if (aLParam) { 1.1136 + lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam)); 1.1137 + lParam = reinterpret_cast<LPARAM>(lParamString); 1.1138 + } 1.1139 + else { 1.1140 + lParamString = nullptr; 1.1141 + lParam = 0; 1.1142 + } 1.1143 +} 1.1144 + 1.1145 +DeferredSettingChangeMessage::~DeferredSettingChangeMessage() 1.1146 +{ 1.1147 + free(lParamString); 1.1148 +} 1.1149 + 1.1150 +DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd, 1.1151 + LPARAM aLParam, 1.1152 + bool aForCalcSize, 1.1153 + WPARAM aWParam) 1.1154 +{ 1.1155 + if (aForCalcSize) { 1.1156 + if (aWParam) { 1.1157 + NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam); 1.1158 + memcpy(&windowPos, arg->lppos, sizeof(windowPos)); 1.1159 + 1.1160 + NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!"); 1.1161 + } 1.1162 + else { 1.1163 + RECT* arg = reinterpret_cast<RECT*>(aLParam); 1.1164 + windowPos.hwnd = aHWnd; 1.1165 + windowPos.hwndInsertAfter = nullptr; 1.1166 + windowPos.x = arg->left; 1.1167 + windowPos.y = arg->top; 1.1168 + windowPos.cx = arg->right - arg->left; 1.1169 + windowPos.cy = arg->bottom - arg->top; 1.1170 + 1.1171 + NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top, 1.1172 + "Negative width or height!"); 1.1173 + } 1.1174 + windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER | 1.1175 + SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING; 1.1176 + } 1.1177 + else { 1.1178 + // Not for WM_NCCALCSIZE 1.1179 + WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam); 1.1180 + memcpy(&windowPos, arg, sizeof(windowPos)); 1.1181 + 1.1182 + NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!"); 1.1183 + 1.1184 + // Windows sends in some private flags sometimes that we can't simply copy. 1.1185 + // Filter here. 1.1186 + UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME | 1.1187 + SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE | 1.1188 + SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | 1.1189 + SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE | 1.1190 + SWP_NOZORDER | SWP_SHOWWINDOW; 1.1191 + windowPos.flags &= mask; 1.1192 + } 1.1193 +} 1.1194 + 1.1195 +void 1.1196 +DeferredWindowPosMessage::Run() 1.1197 +{ 1.1198 + AssertWindowIsNotNeutered(windowPos.hwnd); 1.1199 + if (!IsWindow(windowPos.hwnd)) { 1.1200 + NS_ERROR("Invalid window!"); 1.1201 + return; 1.1202 + } 1.1203 + 1.1204 + if (!IsWindow(windowPos.hwndInsertAfter)) { 1.1205 + NS_WARNING("ZOrder change cannot be honored"); 1.1206 + windowPos.hwndInsertAfter = 0; 1.1207 + windowPos.flags |= SWP_NOZORDER; 1.1208 + } 1.1209 + 1.1210 +#ifdef DEBUG 1.1211 + BOOL ret = 1.1212 +#endif 1.1213 + SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x, 1.1214 + windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags); 1.1215 + NS_ASSERTION(ret, "SetWindowPos failed!"); 1.1216 +} 1.1217 + 1.1218 +DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd, 1.1219 + UINT aMessage, 1.1220 + WPARAM aWParam, 1.1221 + LPARAM aLParam) 1.1222 +: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) 1.1223 +{ 1.1224 + NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!"); 1.1225 + 1.1226 + COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam); 1.1227 + NS_ASSERTION(source, "Should never be null!"); 1.1228 + 1.1229 + copyData.dwData = source->dwData; 1.1230 + copyData.cbData = source->cbData; 1.1231 + 1.1232 + if (source->cbData) { 1.1233 + copyData.lpData = malloc(source->cbData); 1.1234 + if (copyData.lpData) { 1.1235 + memcpy(copyData.lpData, source->lpData, source->cbData); 1.1236 + } 1.1237 + else { 1.1238 + NS_ERROR("Out of memory?!"); 1.1239 + copyData.cbData = 0; 1.1240 + } 1.1241 + } 1.1242 + else { 1.1243 + copyData.lpData = nullptr; 1.1244 + } 1.1245 + 1.1246 + lParam = reinterpret_cast<LPARAM>(©Data); 1.1247 +} 1.1248 + 1.1249 +DeferredCopyDataMessage::~DeferredCopyDataMessage() 1.1250 +{ 1.1251 + free(copyData.lpData); 1.1252 +} 1.1253 + 1.1254 +DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd, 1.1255 + WPARAM aWParam, 1.1256 + LPARAM aLParam) 1.1257 +: hWnd(aHWnd) 1.1258 +{ 1.1259 + index = static_cast<int>(aWParam); 1.1260 + style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew; 1.1261 +} 1.1262 + 1.1263 +void 1.1264 +DeferredStyleChangeMessage::Run() 1.1265 +{ 1.1266 + SetWindowLongPtr(hWnd, index, style); 1.1267 +} 1.1268 + 1.1269 +DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd, 1.1270 + UINT aMessage, 1.1271 + WPARAM aWParam, 1.1272 + LPARAM aLParam) 1.1273 +: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam) 1.1274 +{ 1.1275 + NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!"); 1.1276 +} 1.1277 + 1.1278 +void 1.1279 +DeferredSetIconMessage::Run() 1.1280 +{ 1.1281 + AssertWindowIsNotNeutered(hWnd); 1.1282 + if (!IsWindow(hWnd)) { 1.1283 + NS_ERROR("Invalid window!"); 1.1284 + return; 1.1285 + } 1.1286 + 1.1287 + WNDPROC wndproc = 1.1288 + reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC)); 1.1289 + if (!wndproc) { 1.1290 + NS_ERROR("Invalid window procedure!"); 1.1291 + return; 1.1292 + } 1.1293 + 1.1294 + HICON hOld = reinterpret_cast<HICON>( 1.1295 + CallWindowProc(wndproc, hWnd, message, wParam, lParam)); 1.1296 + if (hOld) { 1.1297 + DestroyIcon(hOld); 1.1298 + } 1.1299 +}