ipc/glue/WindowsMessageLoop.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 et :
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "mozilla/DebugOnly.h"
    10 #include "WindowsMessageLoop.h"
    11 #include "MessageChannel.h"
    13 #include "nsAutoPtr.h"
    14 #include "nsServiceManagerUtils.h"
    15 #include "nsString.h"
    16 #include "nsIXULAppInfo.h"
    18 #include "mozilla/PaintTracker.h"
    20 using namespace mozilla;
    21 using namespace mozilla::ipc;
    22 using namespace mozilla::ipc::windows;
    24 /**
    25  * The Windows-only code below exists to solve a general problem with deadlocks
    26  * that we experience when sending synchronous IPC messages to processes that
    27  * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous
    28  * messages between parent and child HWNDs in multiple circumstances (e.g.
    29  * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled
    30  * by different threads or different processes. Thus we can very easily end up
    31  * in a deadlock by a call stack like the following:
    32  *
    33  * Process A:
    34  *   - CreateWindow(...) creates a "parent" HWND.
    35  *   - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent"
    36  *         HWND over to Process B. Process A blocks until a response is received
    37  *         from Process B.
    38  *
    39  * Process B:
    40  *   - RecvCreateWidget(HWND) gets the "parent" HWND from Process A.
    41  *   - CreateWindow(..., HWND) creates a "child" HWND with the parent from
    42  *         process A.
    43  *   - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent
    44  *         synchronously to Process A. Process B blocks until a response is
    45  *         received from Process A. Process A, however, is blocked and cannot
    46  *         process the message. Both processes are deadlocked.
    47  *
    48  * The example above has a few different workarounds (e.g. setting the
    49  * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is
    50  * persists. Once two HWNDs are parented we must not block their owning
    51  * threads when manipulating either HWND.
    52  *
    53  * Windows requires any application that hosts native HWNDs to always process
    54  * messages or risk deadlock. Given our architecture the only way to meet
    55  * Windows' requirement and allow for synchronous IPC messages is to pump a
    56  * miniature message loop during a sync IPC call. We avoid processing any
    57  * queued messages during the loop (with one exception, see below), but 
    58  * "nonqueued" messages (see 
    59  * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the
    60  * section "Nonqueued messages") cannot be avoided. Those messages are trapped
    61  * in a special window procedure where we can either ignore the message or
    62  * process it in some fashion.
    63  *
    64  * Queued and "non-queued" messages will be processed during Interrupt calls if
    65  * modal UI related api calls block an Interrupt in-call in the child. To prevent
    66  * windows from freezing, and to allow concurrent processing of critical
    67  * events (such as painting), we spin a native event dispatch loop while
    68  * these in-calls are blocked.
    69  */
    71 #if defined(ACCESSIBILITY)
    72 // pulled from accessibility's win utils
    73 extern const wchar_t* kPropNameTabContent;
    74 #endif
    76 // widget related message id constants we need to defer
    77 namespace mozilla {
    78 namespace widget {
    79 extern UINT sAppShellGeckoMsgId;
    80 #ifdef MOZ_METRO
    81 extern UINT sDefaultBrowserMsgId;
    82 #endif
    83 }
    84 }
    86 namespace {
    88 const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
    90 // This isn't defined before Windows XP.
    91 enum { WM_XP_THEMECHANGED = 0x031A };
    93 char16_t gAppMessageWindowName[256] = { 0 };
    94 int32_t gAppMessageWindowNameLength = 0;
    96 nsTArray<HWND>* gNeuteredWindows = nullptr;
    98 typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
    99 DeferredMessageArray* gDeferredMessages = nullptr;
   101 HHOOK gDeferredGetMsgHook = nullptr;
   102 HHOOK gDeferredCallWndProcHook = nullptr;
   104 DWORD gUIThreadId = 0;
   106 LRESULT CALLBACK
   107 DeferredMessageHook(int nCode,
   108                     WPARAM wParam,
   109                     LPARAM lParam)
   110 {
   111   // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
   112   //     WH_GETMESSAGE hook, but they have different parameters. We don't
   113   //     use any of them except nCode which has the same meaning.
   115   // Only run deferred messages if all of these conditions are met:
   116   //   1. The |nCode| indicates that this hook should do something.
   117   //   2. We have deferred messages to run.
   118   //   3. We're not being called from the PeekMessage within the WaitFor*Notify
   119   //      function (indicated with MessageChannel::IsPumpingMessages). We really
   120   //      only want to run after returning to the main event loop.
   121   if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) {
   122     NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
   123                  "These hooks must be set if we're being called!");
   124     NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
   126     // Unset hooks first, in case we reenter below.
   127     UnhookWindowsHookEx(gDeferredGetMsgHook);
   128     UnhookWindowsHookEx(gDeferredCallWndProcHook);
   129     gDeferredGetMsgHook = 0;
   130     gDeferredCallWndProcHook = 0;
   132     // Unset the global and make sure we delete it when we're done here.
   133     nsAutoPtr<DeferredMessageArray> messages(gDeferredMessages);
   134     gDeferredMessages = nullptr;
   136     // Run all the deferred messages in order.
   137     uint32_t count = messages->Length();
   138     for (uint32_t index = 0; index < count; index++) {
   139       messages->ElementAt(index)->Run();
   140     }
   141   }
   143   // Always call the next hook.
   144   return CallNextHookEx(nullptr, nCode, wParam, lParam);
   145 }
   147 void
   148 ScheduleDeferredMessageRun()
   149 {
   150   if (gDeferredMessages &&
   151       !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) {
   152     NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
   154     gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook,
   155                                              nullptr, gUIThreadId);
   156     gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,
   157                                                   DeferredMessageHook, nullptr,
   158                                                   gUIThreadId);
   159     NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
   160                  "Failed to set hooks!");
   161   }
   162 }
   164 LRESULT
   165 ProcessOrDeferMessage(HWND hwnd,
   166                       UINT uMsg,
   167                       WPARAM wParam,
   168                       LPARAM lParam)
   169 {
   170   DeferredMessage* deferred = nullptr;
   172   // Most messages ask for 0 to be returned if the message is processed.
   173   LRESULT res = 0;
   175   switch (uMsg) {
   176     // Messages that can be deferred as-is. These must not contain pointers in
   177     // their wParam or lParam arguments!
   178     case WM_ACTIVATE:
   179     case WM_ACTIVATEAPP:
   180     case WM_CANCELMODE:
   181     case WM_CAPTURECHANGED:
   182     case WM_CHILDACTIVATE:
   183     case WM_DESTROY:
   184     case WM_ENABLE:
   185     case WM_IME_NOTIFY:
   186     case WM_IME_SETCONTEXT:
   187     case WM_KILLFOCUS:
   188     case WM_MOUSEWHEEL:
   189     case WM_NCDESTROY:
   190     case WM_PARENTNOTIFY:
   191     case WM_SETFOCUS:
   192     case WM_SYSCOMMAND:
   193     case WM_DISPLAYCHANGE:
   194     case WM_SHOWWINDOW: // Intentional fall-through.
   195     case WM_XP_THEMECHANGED: {
   196       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
   197       break;
   198     }
   200     case WM_DEVICECHANGE:
   201     case WM_POWERBROADCAST:
   202     case WM_NCACTIVATE: // Intentional fall-through.
   203     case WM_SETCURSOR: {
   204       // Friggin unconventional return value...
   205       res = TRUE;
   206       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
   207       break;
   208     }
   210     case WM_MOUSEACTIVATE: {
   211       res = MA_NOACTIVATE;
   212       deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
   213       break;
   214     }
   216     // These messages need to use the RedrawWindow function to generate the
   217     // right kind of message. We can't simply fake them as the MSDN docs say
   218     // explicitly that paint messages should not be sent by an application.
   219     case WM_ERASEBKGND: {
   220       UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT |
   221                    RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW;
   222       deferred = new DeferredRedrawMessage(hwnd, flags);
   223       break;
   224     }
   226     // This message will generate a WM_PAINT message if there are invalid
   227     // areas.
   228     case WM_PAINT: {
   229       deferred = new DeferredUpdateMessage(hwnd);
   230       break;
   231     }
   233     // This message holds a string in its lParam that we must copy.
   234     case WM_SETTINGCHANGE: {
   235       deferred = new DeferredSettingChangeMessage(hwnd, uMsg, wParam, lParam);
   236       break;
   237     }
   239     // These messages are faked via a call to SetWindowPos.
   240     case WM_WINDOWPOSCHANGED: {
   241       deferred = new DeferredWindowPosMessage(hwnd, lParam);
   242       break;
   243     }
   244     case WM_NCCALCSIZE: {
   245       deferred = new DeferredWindowPosMessage(hwnd, lParam, true, wParam);
   246       break;
   247     }
   249     case WM_COPYDATA: {
   250       deferred = new DeferredCopyDataMessage(hwnd, uMsg, wParam, lParam);
   251       res = TRUE;
   252       break;
   253     }
   255     case WM_STYLECHANGED: {
   256       deferred = new DeferredStyleChangeMessage(hwnd, wParam, lParam);
   257       break;
   258     }
   260     case WM_SETICON: {
   261       deferred = new DeferredSetIconMessage(hwnd, uMsg, wParam, lParam);
   262       break;
   263     }
   265     // Messages that are safe to pass to DefWindowProc go here.
   266     case WM_ENTERIDLE:
   267     case WM_GETICON:
   268     case WM_NCPAINT: // (never trap nc paint events)
   269     case WM_GETMINMAXINFO:
   270     case WM_GETTEXT:
   271     case WM_NCHITTEST:
   272     case WM_STYLECHANGING:  // Intentional fall-through.
   273     case WM_WINDOWPOSCHANGING: { 
   274       return DefWindowProc(hwnd, uMsg, wParam, lParam);
   275     }
   277     // Just return, prevents DefWindowProc from messaging the window
   278     // syncronously with other events, which may be deferred. Prevents 
   279     // random shutdown of aero composition on the window. 
   280     case WM_SYNCPAINT:
   281       return 0;
   283     // This message causes QuickTime to make re-entrant calls.
   284     // Simply discarding it doesn't seem to hurt anything.
   285     case WM_APP-1:
   286       return 0;
   288     default: {
   289       if (uMsg && uMsg == mozilla::widget::sAppShellGeckoMsgId) {
   290         // Widget's registered native event callback
   291         deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
   292 #ifdef MOZ_METRO
   293       } else if (uMsg && uMsg == mozilla::widget::sDefaultBrowserMsgId) {
   294         // Metro widget's system shutdown message
   295         deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
   296 #endif
   297       } else {
   298         // Unknown messages only
   299 #ifdef DEBUG
   300         nsAutoCString log("Received \"nonqueued\" message ");
   301         log.AppendInt(uMsg);
   302         log.AppendLiteral(" during a synchronous IPC message for window ");
   303         log.AppendInt((int64_t)hwnd);
   305         wchar_t className[256] = { 0 };
   306         if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
   307           log.AppendLiteral(" (\"");
   308           log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
   309           log.AppendLiteral("\")");
   310         }
   312         log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
   313                           "window procedure.");
   314         NS_ERROR(log.get());
   315 #endif
   316         return DefWindowProc(hwnd, uMsg, wParam, lParam);
   317       }
   318     }
   319   }
   321   NS_ASSERTION(deferred, "Must have a message here!");
   323   // Create the deferred message array if it doesn't exist already.
   324   if (!gDeferredMessages) {
   325     gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20);
   326     NS_ASSERTION(gDeferredMessages, "Out of memory!");
   327   }
   329   // Save for later. The array takes ownership of |deferred|.
   330   gDeferredMessages->AppendElement(deferred);
   331   return res;
   332 }
   334 } // anonymous namespace
   336 // We need the pointer value of this in PluginInstanceChild.
   337 LRESULT CALLBACK
   338 NeuteredWindowProc(HWND hwnd,
   339                    UINT uMsg,
   340                    WPARAM wParam,
   341                    LPARAM lParam)
   342 {
   343   WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
   344   if (!oldWndProc) {
   345     // We should really never ever get here.
   346     NS_ERROR("No old wndproc!");
   347     return DefWindowProc(hwnd, uMsg, wParam, lParam);
   348   }
   350   // See if we care about this message. We may either ignore it, send it to
   351   // DefWindowProc, or defer it for later.
   352   return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam);
   353 }
   355 namespace {
   357 static bool
   358 WindowIsDeferredWindow(HWND hWnd)
   359 {
   360   if (!IsWindow(hWnd)) {
   361     NS_WARNING("Window has died!");
   362     return false;
   363   }
   365   char16_t buffer[256] = { 0 };
   366   int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1);
   367   if (length <= 0) {
   368     NS_WARNING("Failed to get class name!");
   369     return false;
   370   }
   372 #if defined(ACCESSIBILITY)
   373   // Tab content creates a window that responds to accessible WM_GETOBJECT
   374   // calls. This window can safely be ignored.
   375   if (::GetPropW(hWnd, kPropNameTabContent)) {
   376     return false;
   377   }
   378 #endif
   380   // Common mozilla windows we must defer messages to.
   381   nsDependentString className(buffer, length);
   382   if (StringBeginsWith(className, NS_LITERAL_STRING("Mozilla")) ||
   383       StringBeginsWith(className, NS_LITERAL_STRING("Gecko")) ||
   384       className.EqualsLiteral("nsToolkitClass") ||
   385       className.EqualsLiteral("nsAppShell:EventWindowClass")) {
   386     return true;
   387   }
   389 #ifdef MOZ_METRO
   390   // immersive UI ICoreWindow
   391   if (className.EqualsLiteral("Windows.UI.Core.CoreWindow")) {
   392     return true;
   393   }
   394 #endif
   396   // Plugin windows that can trigger ipc calls in child:
   397   // 'ShockwaveFlashFullScreen' - flash fullscreen window
   398   // 'QTNSHIDDEN' - QuickTime
   399   // 'AGFullScreenWinClass' - silverlight fullscreen window
   400   if (className.EqualsLiteral("ShockwaveFlashFullScreen") ||
   401       className.EqualsLiteral("QTNSHIDDEN") ||
   402       className.EqualsLiteral("AGFullScreenWinClass")) {
   403     return true;
   404   }
   406   // Google Earth bridging msg window between the plugin instance and a separate
   407   // earth process. The earth process can trigger a plugin incall on the browser
   408   // at any time, which is badness if the instance is already making an incall.
   409   if (className.EqualsLiteral("__geplugin_bridge_window__")) {
   410     return true;
   411   }
   413   // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the
   414   // toolkit app's name. It's pretty expensive to calculate this so we only try
   415   // once.
   416   if (gAppMessageWindowNameLength == 0) {
   417     nsCOMPtr<nsIXULAppInfo> appInfo =
   418       do_GetService("@mozilla.org/xre/app-info;1");
   419     if (appInfo) {
   420       nsAutoCString appName;
   421       if (NS_SUCCEEDED(appInfo->GetName(appName))) {
   422         appName.Append("MessageWindow");
   423         nsDependentString windowName(gAppMessageWindowName);
   424         CopyUTF8toUTF16(appName, windowName);
   425         gAppMessageWindowNameLength = windowName.Length();
   426       }
   427     }
   429     // Don't try again if that failed.
   430     if (gAppMessageWindowNameLength == 0) {
   431       gAppMessageWindowNameLength = -1;
   432     }
   433   }
   435   if (gAppMessageWindowNameLength != -1 &&
   436       className.Equals(nsDependentString(gAppMessageWindowName,
   437                                          gAppMessageWindowNameLength))) {
   438     return true;
   439   }
   441   return false;
   442 }
   444 bool
   445 NeuterWindowProcedure(HWND hWnd)
   446 {
   447   if (!WindowIsDeferredWindow(hWnd)) {
   448     // Some other kind of window, skip.
   449     return false;
   450   }
   452   NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!");
   454   // It's possible to get nullptr out of SetWindowLongPtr, and the only way to
   455   // know if that's a valid old value is to use GetLastError. Clear the error
   456   // here so we can tell.
   457   SetLastError(ERROR_SUCCESS);
   459   LONG_PTR currentWndProc =
   460     SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc);
   461   if (!currentWndProc) {
   462     if (ERROR_SUCCESS == GetLastError()) {
   463       // No error, so we set something and must therefore reset it.
   464       SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
   465     }
   466     return false;
   467   }
   469   NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
   470                "This shouldn't be possible!");
   472   if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
   473     // Cleanup
   474     NS_WARNING("SetProp failed!");
   475     SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
   476     RemoveProp(hWnd, kOldWndProcProp);
   477     return false;
   478   }
   480   return true;
   481 }
   483 void
   484 RestoreWindowProcedure(HWND hWnd)
   485 {
   486   NS_ASSERTION(WindowIsDeferredWindow(hWnd),
   487                "Not a deferred window, this shouldn't be in our list!");
   488   LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp);
   489   if (oldWndProc) {
   490     NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
   491                  "This shouldn't be possible!");
   493     DebugOnly<LONG_PTR> currentWndProc =
   494       SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
   495     NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
   496                  "This should never be switched out from under us!");
   497   }
   498   RemoveProp(hWnd, kOldWndProcProp);
   499 }
   501 LRESULT CALLBACK
   502 CallWindowProcedureHook(int nCode,
   503                         WPARAM wParam,
   504                         LPARAM lParam)
   505 {
   506   if (nCode >= 0) {
   507     NS_ASSERTION(gNeuteredWindows, "This should never be null!");
   509     HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
   511     if (!gNeuteredWindows->Contains(hWnd) && NeuterWindowProcedure(hWnd)) {
   512       if (!gNeuteredWindows->AppendElement(hWnd)) {
   513         NS_ERROR("Out of memory!");
   514         RestoreWindowProcedure(hWnd);
   515       }
   516     }
   517   }
   518   return CallNextHookEx(nullptr, nCode, wParam, lParam);
   519 }
   521 inline void
   522 AssertWindowIsNotNeutered(HWND hWnd)
   523 {
   524 #ifdef DEBUG
   525   // Make sure our neutered window hook isn't still in place.
   526   LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
   527   NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!");
   528 #endif
   529 }
   531 void
   532 UnhookNeuteredWindows()
   533 {
   534   if (!gNeuteredWindows)
   535     return;
   536   uint32_t count = gNeuteredWindows->Length();
   537   for (uint32_t index = 0; index < count; index++) {
   538     RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
   539   }
   540   gNeuteredWindows->Clear();
   541 }
   543 // This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
   544 // value for GetTickCount(), which is something like 50 days). It uses the
   545 // cheapest (and least accurate) method supported by Windows 2000.
   547 struct TimeoutData
   548 {
   549   DWORD startTicks;
   550   DWORD targetTicks;
   551 };
   553 void
   554 InitTimeoutData(TimeoutData* aData,
   555                 int32_t aTimeoutMs)
   556 {
   557   aData->startTicks = GetTickCount();
   558   if (!aData->startTicks) {
   559     // How unlikely is this!
   560     aData->startTicks++;
   561   }
   562   aData->targetTicks = aData->startTicks + aTimeoutMs;
   563 }
   566 bool
   567 TimeoutHasExpired(const TimeoutData& aData)
   568 {
   569   if (!aData.startTicks) {
   570     return false;
   571   }
   573   DWORD now = GetTickCount();
   575   if (aData.targetTicks < aData.startTicks) {
   576     // Overflow
   577     return now < aData.startTicks && now >= aData.targetTicks;
   578   }
   579   return now >= aData.targetTicks;
   580 }
   582 } // anonymous namespace
   584 namespace mozilla {
   585 namespace ipc {
   586 namespace windows {
   588 void
   589 InitUIThread()
   590 {
   591   // If we aren't setup before a call to NotifyWorkerThread, we'll hang
   592   // on startup.
   593   if (!gUIThreadId) {
   594     gUIThreadId = GetCurrentThreadId();
   595   }
   596   MOZ_ASSERT(gUIThreadId);
   597   MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
   598                "Called InitUIThread multiple times on different threads!");
   599 }
   601 } // namespace windows
   602 } // namespace ipc
   603 } // namespace mozilla
   605 // See SpinInternalEventLoop below
   606 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
   607   : mInterrupt(interrupt)
   608   , mSpinNestedEvents(false)
   609   , mListenerNotified(false)
   610   , mChannel(channel)
   611   , mPrev(mChannel->mTopFrame)
   612   , mStaticPrev(sStaticTopFrame)
   613 {
   614   // Only track stack frames when Windows message deferral behavior
   615   // is request for the channel.
   616   if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
   617     return;
   618   }
   620   mChannel->mTopFrame = this;
   621   sStaticTopFrame = this;
   623   if (!mStaticPrev) {
   624     NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
   625     gNeuteredWindows = new nsAutoTArray<HWND, 20>();
   626     NS_ASSERTION(gNeuteredWindows, "Out of memory!");
   627   }
   628 }
   630 MessageChannel::SyncStackFrame::~SyncStackFrame()
   631 {
   632   if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
   633     return;
   634   }
   636   NS_ASSERTION(this == mChannel->mTopFrame,
   637                "Mismatched interrupt stack frames");
   638   NS_ASSERTION(this == sStaticTopFrame,
   639                "Mismatched static Interrupt stack frames");
   641   mChannel->mTopFrame = mPrev;
   642   sStaticTopFrame = mStaticPrev;
   644   if (!mStaticPrev) {
   645     NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
   646     delete gNeuteredWindows;
   647     gNeuteredWindows = nullptr;
   648   }
   649 }
   651 MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
   653 // nsAppShell's notification that gecko events are being processed.
   654 // If we are here and there is an Interrupt Incall active, we are spinning
   655 // a nested gecko event loop. In which case the remote process needs
   656 // to know about it.
   657 void /* static */
   658 MessageChannel::NotifyGeckoEventDispatch()
   659 {
   660   // sStaticTopFrame is only valid for Interrupt channels
   661   if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
   662     return;
   664   sStaticTopFrame->mListenerNotified = true;
   665   MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
   666   channel->Listener()->ProcessRemoteNativeEventsInInterruptCall();
   667 }
   669 // invoked by the module that receives the spin event loop
   670 // message.
   671 void
   672 MessageChannel::ProcessNativeEventsInInterruptCall()
   673 {
   674   NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
   675                "Shouldn't be on a non-main thread in here!");
   676   if (!mTopFrame) {
   677     NS_ERROR("Spin logic error: no Interrupt frame");
   678     return;
   679   }
   681   mTopFrame->mSpinNestedEvents = true;
   682 }
   684 // Spin loop is called in place of WaitFor*Notify when modal ui is being shown
   685 // in a child. There are some intricacies in using it however. Spin loop is
   686 // enabled for a particular Interrupt frame by the client calling
   687 // MessageChannel::ProcessNativeEventsInInterrupt().
   688 // This call can be nested for multiple Interrupt frames in a single plugin or 
   689 // multiple unrelated plugins.
   690 void
   691 MessageChannel::SpinInternalEventLoop()
   692 {
   693   if (mozilla::PaintTracker::IsPainting()) {
   694     NS_RUNTIMEABORT("Don't spin an event loop while painting.");
   695   }
   697   NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents,
   698                "Spinning incorrectly");
   700   // Nested windows event loop we trigger when the child enters into modal
   701   // event loops.
   703   // Note, when we return, we always reset the notify worker event. So there's
   704   // no need to reset it on return here.
   706   do {
   707     MSG msg = { 0 };
   709     // Don't get wrapped up in here if the child connection dies.
   710     {
   711       MonitorAutoLock lock(*mMonitor);
   712       if (!Connected()) {
   713         return;
   714       }
   715     }
   717     // Retrieve window or thread messages
   718     if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
   719       // The child UI should have been destroyed before the app is closed, in
   720       // which case, we should never get this here.
   721       if (msg.message == WM_QUIT) {
   722           NS_ERROR("WM_QUIT received in SpinInternalEventLoop!");
   723       } else {
   724           TranslateMessage(&msg);
   725           ::DispatchMessageW(&msg);
   726           return;
   727       }
   728     }
   730     // Note, give dispatching windows events priority over checking if
   731     // mEvent is signaled, otherwise heavy ipc traffic can cause jittery
   732     // playback of video. We'll exit out on each disaptch above, so ipc
   733     // won't get starved.
   735     // Wait for UI events or a signal from the io thread.
   736     DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
   737                                              QS_ALLINPUT);
   738     if (result == WAIT_OBJECT_0) {
   739       // Our NotifyWorkerThread event was signaled
   740       return;
   741     }
   742   } while (true);
   743 }
   745 static inline bool
   746 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
   747 {
   748   return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
   749     (aTimeout <= (PR_IntervalNow() - aStart));
   750 }
   752 bool
   753 MessageChannel::WaitForSyncNotify()
   754 {
   755   mMonitor->AssertCurrentThreadOwns();
   757   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
   759   // Use a blocking wait if this channel does not require
   760   // Windows message deferral behavior.
   761   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
   762     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
   763                              PR_INTERVAL_NO_TIMEOUT :
   764                              PR_MillisecondsToInterval(mTimeoutMs);
   765     // XXX could optimize away this syscall for "no timeout" case if desired
   766     PRIntervalTime waitStart = 0;
   768     if (timeout != PR_INTERVAL_NO_TIMEOUT) {
   769       waitStart = PR_IntervalNow();
   770     }
   772     mIsSyncWaitingOnNonMainThread = true;
   774     mMonitor->Wait(timeout);
   776     mIsSyncWaitingOnNonMainThread = false;
   778     // If the timeout didn't expire, we know we received an event. The
   779     // converse is not true.
   780     return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ?
   781                         false : IsTimeoutExpired(waitStart, timeout));
   782   }
   784   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
   785                "Shouldn't be here for channels that don't use message deferral!");
   786   NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
   787                "Top frame is not a sync frame!");
   789   MonitorAutoUnlock unlock(*mMonitor);
   791   bool timedout = false;
   793   UINT_PTR timerId = 0;
   794   TimeoutData timeoutData = { 0 };
   796   if (mTimeoutMs != kNoTimeout) {
   797     InitTimeoutData(&timeoutData, mTimeoutMs);
   799     // We only do this to ensure that we won't get stuck in
   800     // MsgWaitForMultipleObjects below.
   801     timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
   802     NS_ASSERTION(timerId, "SetTimer failed!");
   803   }
   805   // Setup deferred processing of native events while we wait for a response.
   806   NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
   807                "Shouldn't be pumping already!");
   809   MessageChannel::SetIsPumpingMessages(true);
   810   HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
   811                                       nullptr, gUIThreadId);
   812   NS_ASSERTION(windowHook, "Failed to set hook!");
   814   {
   815     while (1) {
   816       MSG msg = { 0 };
   817       // Don't get wrapped up in here if the child connection dies.
   818       {
   819         MonitorAutoLock lock(*mMonitor);
   820         if (!Connected()) {
   821           break;
   822         }
   823       }
   825       // Wait until we have a message in the queue. MSDN docs are a bit unclear
   826       // but it seems that windows from two different threads (and it should be
   827       // noted that a thread in another process counts as a "different thread")
   828       // will implicitly have their message queues attached if they are parented
   829       // to one another. This wait call, then, will return for a message
   830       // delivered to *either* thread.
   831       DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
   832                                                QS_ALLINPUT);
   833       if (result == WAIT_OBJECT_0) {
   834         // Our NotifyWorkerThread event was signaled
   835         ResetEvent(mEvent);
   836         break;
   837       } else
   838       if (result != (WAIT_OBJECT_0 + 1)) {
   839         NS_ERROR("Wait failed!");
   840         break;
   841       }
   843       if (TimeoutHasExpired(timeoutData)) {
   844         // A timeout was specified and we've passed it. Break out.
   845         timedout = true;
   846         break;
   847       }
   849       // The only way to know on which thread the message was delivered is to
   850       // use some logic on the return values of GetQueueStatus and PeekMessage.
   851       // PeekMessage will return false if there are no "queued" messages, but it
   852       // will run all "nonqueued" messages before returning. So if PeekMessage
   853       // returns false and there are no "nonqueued" messages that were run then
   854       // we know that the message we woke for was intended for a window on
   855       // another thread.
   856       bool haveSentMessagesPending =
   857         (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
   859       // This PeekMessage call will actually process all "nonqueued" messages
   860       // that are pending before returning. If we have "nonqueued" messages
   861       // pending then we should have switched out all the window procedures
   862       // above. In that case this PeekMessage call won't actually cause any
   863       // mozilla code (or plugin code) to run.
   865       // If the following PeekMessage call fails to return a message for us (and
   866       // returns false) and we didn't run any "nonqueued" messages then we must
   867       // have woken up for a message designated for a window in another thread.
   868       // If we loop immediately then we could enter a tight loop, so we'll give
   869       // up our time slice here to let the child process its message.
   870       if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
   871           !haveSentMessagesPending) {
   872         // Message was for child, we should wait a bit.
   873         SwitchToThread();
   874       }
   875     }
   876   }
   878   // Unhook the neutered window procedure hook.
   879   UnhookWindowsHookEx(windowHook);
   881   // Unhook any neutered windows procedures so messages can be delivered
   882   // normally.
   883   UnhookNeuteredWindows();
   885   // Before returning we need to set a hook to run any deferred messages that
   886   // we received during the IPC call. The hook will unset itself as soon as
   887   // someone else calls GetMessage, PeekMessage, or runs code that generates
   888   // a "nonqueued" message.
   889   ScheduleDeferredMessageRun();
   891   if (timerId) {
   892     KillTimer(nullptr, timerId);
   893   }
   895   MessageChannel::SetIsPumpingMessages(false);
   897   return WaitResponse(timedout);
   898 }
   900 bool
   901 MessageChannel::WaitForInterruptNotify()
   902 {
   903   mMonitor->AssertCurrentThreadOwns();
   905   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
   907   // Re-use sync notification wait code if this channel does not require
   908   // Windows message deferral behavior. 
   909   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
   910     return WaitForSyncNotify();
   911   }
   913   if (!InterruptStackDepth()) {
   914     // There is currently no way to recover from this condition.
   915     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   916   }
   918   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
   919                "Shouldn't be here for channels that don't use message deferral!");
   920   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
   921                "Top frame is not a sync frame!");
   923   MonitorAutoUnlock unlock(*mMonitor);
   925   bool timedout = false;
   927   UINT_PTR timerId = 0;
   928   TimeoutData timeoutData = { 0 };
   930   // windowHook is used as a flag variable for the loop below: if it is set
   931   // and we start to spin a nested event loop, we need to clear the hook and
   932   // process deferred/pending messages.
   933   // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
   934   HHOOK windowHook = nullptr;
   936   while (1) {
   937     NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
   938                  "windowHook out of sync with reality");
   940     if (mTopFrame->mSpinNestedEvents) {
   941       if (windowHook) {
   942         UnhookWindowsHookEx(windowHook);
   943         windowHook = nullptr;
   945         if (timerId) {
   946           KillTimer(nullptr, timerId);
   947           timerId = 0;
   948         }
   950         // Used by widget to assert on incoming native events
   951         MessageChannel::SetIsPumpingMessages(false);
   953         // Unhook any neutered windows procedures so messages can be delievered
   954         // normally.
   955         UnhookNeuteredWindows();
   957         // Send all deferred "nonqueued" message to the intended receiver.
   958         // We're dropping into SpinInternalEventLoop so we should be fairly
   959         // certain these will get delivered soohn.
   960         ScheduleDeferredMessageRun();
   961       }
   962       SpinInternalEventLoop();
   963       ResetEvent(mEvent);
   964       return true;
   965     }
   967     if (!windowHook) {
   968       MessageChannel::SetIsPumpingMessages(true);
   969       windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
   970                                     nullptr, gUIThreadId);
   971       NS_ASSERTION(windowHook, "Failed to set hook!");
   973       NS_ASSERTION(!timerId, "Timer already initialized?");
   975       if (mTimeoutMs != kNoTimeout) {
   976         InitTimeoutData(&timeoutData, mTimeoutMs);
   977         timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
   978         NS_ASSERTION(timerId, "SetTimer failed!");
   979       }
   980     }
   982     MSG msg = { 0 };
   984     // Don't get wrapped up in here if the child connection dies.
   985     {
   986       MonitorAutoLock lock(*mMonitor);
   987       if (!Connected()) {
   988         break;
   989       }
   990     }
   992     DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
   993                                              QS_ALLINPUT);
   994     if (result == WAIT_OBJECT_0) {
   995       // Our NotifyWorkerThread event was signaled
   996       ResetEvent(mEvent);
   997       break;
   998     } else
   999     if (result != (WAIT_OBJECT_0 + 1)) {
  1000       NS_ERROR("Wait failed!");
  1001       break;
  1004     if (TimeoutHasExpired(timeoutData)) {
  1005       // A timeout was specified and we've passed it. Break out.
  1006       timedout = true;
  1007       break;
  1010     // See MessageChannel's WaitFor*Notify for details.
  1011     bool haveSentMessagesPending =
  1012       (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
  1014     // PeekMessage markes the messages as "old" so that they don't wake up
  1015     // MsgWaitForMultipleObjects every time.
  1016     if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
  1017         !haveSentMessagesPending) {
  1018       // Message was for child, we should wait a bit.
  1019       SwitchToThread();
  1023   if (windowHook) {
  1024     // Unhook the neutered window procedure hook.
  1025     UnhookWindowsHookEx(windowHook);
  1027     // Unhook any neutered windows procedures so messages can be delivered
  1028     // normally.
  1029     UnhookNeuteredWindows();
  1031     // Before returning we need to set a hook to run any deferred messages that
  1032     // we received during the IPC call. The hook will unset itself as soon as
  1033     // someone else calls GetMessage, PeekMessage, or runs code that generates
  1034     // a "nonqueued" message.
  1035     ScheduleDeferredMessageRun();
  1037     if (timerId) {
  1038       KillTimer(nullptr, timerId);
  1042   MessageChannel::SetIsPumpingMessages(false);
  1044   return WaitResponse(timedout);
  1047 void
  1048 MessageChannel::NotifyWorkerThread()
  1050   mMonitor->AssertCurrentThreadOwns();
  1052   if (mIsSyncWaitingOnNonMainThread) {
  1053     mMonitor->Notify();
  1054     return;
  1057   NS_ASSERTION(mEvent, "No signal event to set, this is really bad!");
  1058   if (!SetEvent(mEvent)) {
  1059     NS_WARNING("Failed to set NotifyWorkerThread event!");
  1063 void
  1064 DeferredSendMessage::Run()
  1066   AssertWindowIsNotNeutered(hWnd);
  1067   if (!IsWindow(hWnd)) {
  1068     NS_ERROR("Invalid window!");
  1069     return;
  1072   WNDPROC wndproc =
  1073     reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
  1074   if (!wndproc) {
  1075     NS_ERROR("Invalid window procedure!");
  1076     return;
  1079   CallWindowProc(wndproc, hWnd, message, wParam, lParam);
  1082 void
  1083 DeferredRedrawMessage::Run()
  1085   AssertWindowIsNotNeutered(hWnd);
  1086   if (!IsWindow(hWnd)) {
  1087     NS_ERROR("Invalid window!");
  1088     return;
  1091 #ifdef DEBUG
  1092   BOOL ret =
  1093 #endif
  1094   RedrawWindow(hWnd, nullptr, nullptr, flags);
  1095   NS_ASSERTION(ret, "RedrawWindow failed!");
  1098 DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd)
  1100   mWnd = aHWnd;
  1101   if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) {
  1102     memset(&mUpdateRect, 0, sizeof(RECT));
  1103     return;
  1105   ValidateRect(mWnd, &mUpdateRect);
  1108 void
  1109 DeferredUpdateMessage::Run()
  1111   AssertWindowIsNotNeutered(mWnd);
  1112   if (!IsWindow(mWnd)) {
  1113     NS_ERROR("Invalid window!");
  1114     return;
  1117   InvalidateRect(mWnd, &mUpdateRect, FALSE);
  1118 #ifdef DEBUG
  1119   BOOL ret =
  1120 #endif
  1121   UpdateWindow(mWnd);
  1122   NS_ASSERTION(ret, "UpdateWindow failed!");
  1125 DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd,
  1126                                                            UINT aMessage,
  1127                                                            WPARAM aWParam,
  1128                                                            LPARAM aLParam)
  1129 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
  1131   NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!");
  1132   if (aLParam) {
  1133     lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam));
  1134     lParam = reinterpret_cast<LPARAM>(lParamString);
  1136   else {
  1137     lParamString = nullptr;
  1138     lParam = 0;
  1142 DeferredSettingChangeMessage::~DeferredSettingChangeMessage()
  1144   free(lParamString);
  1147 DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd,
  1148                                                    LPARAM aLParam,
  1149                                                    bool aForCalcSize,
  1150                                                    WPARAM aWParam)
  1152   if (aForCalcSize) {
  1153     if (aWParam) {
  1154       NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam);
  1155       memcpy(&windowPos, arg->lppos, sizeof(windowPos));
  1157       NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
  1159     else {
  1160       RECT* arg = reinterpret_cast<RECT*>(aLParam);
  1161       windowPos.hwnd = aHWnd;
  1162       windowPos.hwndInsertAfter = nullptr;
  1163       windowPos.x = arg->left;
  1164       windowPos.y = arg->top;
  1165       windowPos.cx = arg->right - arg->left;
  1166       windowPos.cy = arg->bottom - arg->top;
  1168       NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top,
  1169                    "Negative width or height!");
  1171     windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER |
  1172                       SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING;
  1174   else {
  1175     // Not for WM_NCCALCSIZE
  1176     WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam);
  1177     memcpy(&windowPos, arg, sizeof(windowPos));
  1179     NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
  1181     // Windows sends in some private flags sometimes that we can't simply copy.
  1182     // Filter here.
  1183     UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME |
  1184                 SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE |
  1185                 SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
  1186                 SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE |
  1187                 SWP_NOZORDER | SWP_SHOWWINDOW;
  1188     windowPos.flags &= mask;
  1192 void
  1193 DeferredWindowPosMessage::Run()
  1195   AssertWindowIsNotNeutered(windowPos.hwnd);
  1196   if (!IsWindow(windowPos.hwnd)) {
  1197     NS_ERROR("Invalid window!");
  1198     return;
  1201   if (!IsWindow(windowPos.hwndInsertAfter)) {
  1202     NS_WARNING("ZOrder change cannot be honored");
  1203     windowPos.hwndInsertAfter = 0;
  1204     windowPos.flags |= SWP_NOZORDER;
  1207 #ifdef DEBUG
  1208   BOOL ret = 
  1209 #endif
  1210   SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x,
  1211                windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags);
  1212   NS_ASSERTION(ret, "SetWindowPos failed!");
  1215 DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd,
  1216                                                  UINT aMessage,
  1217                                                  WPARAM aWParam,
  1218                                                  LPARAM aLParam)
  1219 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
  1221   NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!");
  1223   COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam);
  1224   NS_ASSERTION(source, "Should never be null!");
  1226   copyData.dwData = source->dwData;
  1227   copyData.cbData = source->cbData;
  1229   if (source->cbData) {
  1230     copyData.lpData = malloc(source->cbData);
  1231     if (copyData.lpData) {
  1232       memcpy(copyData.lpData, source->lpData, source->cbData);
  1234     else {
  1235       NS_ERROR("Out of memory?!");
  1236       copyData.cbData = 0;
  1239   else {
  1240     copyData.lpData = nullptr;
  1243   lParam = reinterpret_cast<LPARAM>(&copyData);
  1246 DeferredCopyDataMessage::~DeferredCopyDataMessage()
  1248   free(copyData.lpData);
  1251 DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd,
  1252                                                        WPARAM aWParam,
  1253                                                        LPARAM aLParam)
  1254 : hWnd(aHWnd)
  1256   index = static_cast<int>(aWParam);
  1257   style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew;
  1260 void
  1261 DeferredStyleChangeMessage::Run()
  1263   SetWindowLongPtr(hWnd, index, style);
  1266 DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd,
  1267                                                UINT aMessage,
  1268                                                WPARAM aWParam,
  1269                                                LPARAM aLParam)
  1270 : DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
  1272   NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!");
  1275 void
  1276 DeferredSetIconMessage::Run()
  1278   AssertWindowIsNotNeutered(hWnd);
  1279   if (!IsWindow(hWnd)) {
  1280     NS_ERROR("Invalid window!");
  1281     return;
  1284   WNDPROC wndproc =
  1285     reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
  1286   if (!wndproc) {
  1287     NS_ERROR("Invalid window procedure!");
  1288     return;
  1291   HICON hOld = reinterpret_cast<HICON>(
  1292     CallWindowProc(wndproc, hWnd, message, wParam, lParam));
  1293   if (hOld) {
  1294     DestroyIcon(hOld);

mercurial