ipc/glue/WindowsMessageLoop.cpp

changeset 0
6474c204b198
     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>(&copyData);
  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 +}

mercurial