Bug 648935 - Process native events during calls to WaitForNotify in the child process when nested native event loops are detected in the parent. r=bsmedberg.
authorJim Mathies <jmathies@mozilla.com>
Wed, 18 May 2011 06:57:08 -0500
changeset 69669 d40eac0106f52ef4a6834616b7a53cefe62c0af0
parent 69668 02f44011950845aff53434f43fd54c0031bad435
child 69670 f5ac9367f1e15ca931afebc8b986e32ad11fc21d
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs648935
milestone6.0a1
Bug 648935 - Process native events during calls to WaitForNotify in the child process when nested native event loops are detected in the parent. r=bsmedberg.
dom/plugins/ipc/PPluginModule.ipdl
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleChild.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
ipc/glue/RPCChannel.cpp
ipc/glue/RPCChannel.h
ipc/glue/SyncChannel.h
ipc/glue/WindowsMessageLoop.cpp
ipc/ipdl/ipdl/lower.py
widget/src/windows/nsAppShell.cpp
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -63,16 +63,20 @@ both:
    * may be called by either child or parent. If a race occurs by calling the
    * constructor with the same string or int argument then we create two actors
    * and detect the second instance in the child. We prevent the parent's actor
    * from leaking out to plugin code and only allow the child's to be used.
    */
   async PPluginIdentifier(nsCString aString,
                           int32_t aInt);
 
+  // Window-specific message which instructs the RPC mechanism to enter
+  // a nested event loop for the current RPC call.
+  async ProcessNativeEventsInRPCCall();
+
 child:
   // Forces the child process to update its plugin function table.
   rpc NP_GetEntryPoints()
     returns (NPError rv);
 
   // Return the plugin's thread ID, if it can be found.
   rpc NP_Initialize()
     returns (NativeThreadId tid, NPError rv);
@@ -118,20 +122,16 @@ parent:
              bool aBoolVal);
 
   // Wake up and process a few native events.  Periodically called by
   // Gtk-specific code upon detecting that the plugin process has
   // entered a nested event loop.  If the browser doesn't process
   // native events, then "livelock" and some other glitches can occur.
   rpc ProcessSomeEvents();
 
-  // Window-specific message which instructs the RPC mechanism to enter
-  // a nested event loop for the current RPC call.
-  async ProcessNativeEventsInRPCCall();
-
   sync AppendNotesToCrashReport(nsCString aNotes);
 
   // OS X Specific calls to manage the plugin's window
   // when interposing system calls.
   async PluginShowWindow(uint32_t aWindowId, bool aModal,
                          int32_t aX, int32_t aY,
                          size_t aWidth, size_t aHeight);
   async PluginHideWindow(uint32_t aWindowId);
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1736,17 +1736,17 @@ PluginInstanceParent::AnswerPluginFocusC
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
     // Currently only in use on windows - an rpc event we receive from the
     // child when it's plugin window (or one of it's children) receives keyboard
     // focus. We forward the event down to widget so the dom/focus manager can
     // be updated.
 #if defined(OS_WIN)
-    AutoRestore<bool> ar(mInAnswerFocusChange)
+    AutoRestore<bool> ar(mInAnswerFocusChange);
     mInAnswerFocusChange = true;
     ::SendMessage(mPluginHWND, gOOPPPluginFocusEvent, gotFocus ? 1 : 0, 0);
     return true;
 #else
     NS_NOTREACHED("PluginInstanceParent::AnswerPluginFocusChange not implemented!");
     return false;
 #endif
 }
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2290,14 +2290,28 @@ PluginModuleChild::ResetEventHooks()
         UnhookWindowsHookEx(mNestedEventHook);
     mNestedEventHook = NULL;
     if (mGlobalCallWndProcHook)
         UnhookWindowsHookEx(mGlobalCallWndProcHook);
     mGlobalCallWndProcHook = NULL;
 }
 #endif
 
+bool
+PluginModuleChild::RecvProcessNativeEventsInRPCCall()
+{
+    PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(OS_WIN)
+    ProcessNativeEventsInRPCCall();
+    return true;
+#else
+    NS_RUNTIMEABORT(
+        "PluginModuleChild::RecvProcessNativeEventsInRPCCall not implemented!");
+    return false;
+#endif
+}
+
 #ifdef OS_MACOSX
 void
 PluginModuleChild::ProcessNativeEvents() {
     CallProcessSomeEvents();    
 }
 #endif
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -152,16 +152,19 @@ protected:
     virtual bool
     AnswerNPP_GetSitesWithData(InfallibleTArray<nsCString>* aResult);
 
     virtual void
     ActorDestroy(ActorDestroyReason why);
 
     NS_NORETURN void QuickExit();
 
+    NS_OVERRIDE virtual bool
+    RecvProcessNativeEventsInRPCCall();
+
 public:
     PluginModuleChild();
     virtual ~PluginModuleChild();
 
     // aPluginFilename is UTF8, not native-charset!
     bool Init(const std::string& aPluginFilename,
               base::ProcessHandle aParentProcessHandle,
               MessageLoop* aIOLoop,
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -995,21 +995,32 @@ bool
 PluginModuleParent::RecvProcessNativeEventsInRPCCall()
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 #if defined(OS_WIN)
     ProcessNativeEventsInRPCCall();
     return true;
 #else
     NS_NOTREACHED(
-        "PluginInstanceParent::RecvProcessNativeEventsInRPCCall not implemented!");
+        "PluginModuleParent::RecvProcessNativeEventsInRPCCall not implemented!");
     return false;
 #endif
 }
 
+void
+PluginModuleParent::ProcessRemoteNativeEventsInRPCCall()
+{
+#if defined(OS_WIN)
+    SendProcessNativeEventsInRPCCall();
+    return;
+#endif
+    NS_NOTREACHED(
+        "PluginModuleParent::ProcessRemoteNativeEventsInRPCCall not implemented!");
+}
+
 bool
 PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
                                          const int32_t& aX, const int32_t& aY,
                                          const size_t& aWidth, const size_t& aHeight)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 #if defined(XP_MACOSX)
     CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -134,16 +134,18 @@ public:
 
     bool OkToCleanup() const {
         return !IsOnCxxStack();
     }
 
     PPluginIdentifierParent*
     GetIdentifierForNPIdentifier(NPIdentifier aIdentifier);
 
+    void ProcessRemoteNativeEventsInRPCCall();
+
 #ifdef OS_MACOSX
     void AddToRefreshTimer(PluginInstanceParent *aInstance);
     void RemoveFromRefreshTimer(PluginInstanceParent *aInstance);
 #endif
 
 protected:
     NS_OVERRIDE
     virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
--- a/ipc/glue/RPCChannel.cpp
+++ b/ipc/glue/RPCChannel.cpp
@@ -490,16 +490,20 @@ RPCChannel::Incall(const Message& call, 
         }
 
         // we "lost" and need to process the other side's in-call.
         // don't need to fix up the mRemoteStackDepthGuess here,
         // because we're just about to increment it in DispatchCall(),
         // which will make it correct again
     }
 
+#ifdef OS_WIN
+    SyncStackFrame frame(this, true);
+#endif
+
     DispatchIncall(call);
 }
 
 void
 RPCChannel::DispatchIncall(const Message& call)
 {
     AssertWorkerThread();
     mMonitor.AssertNotCurrentThreadOwns();
--- a/ipc/glue/RPCChannel.h
+++ b/ipc/glue/RPCChannel.h
@@ -106,16 +106,17 @@ public:
             NS_RUNTIMEABORT("default impl shouldn't be invoked");
         }
 
         virtual RacyRPCPolicy MediateRPCRace(const Message& parent,
                                              const Message& child)
         {
             return RRPChildWins;
         }
+        virtual void ProcessRemoteNativeEventsInRPCCall() {};
     };
 
     RPCChannel(RPCListener* aListener);
 
     virtual ~RPCChannel();
 
     NS_OVERRIDE
     void Clear();
@@ -173,16 +174,17 @@ public:
      *
      * @note This method is used on Windows when we detect that an outbound
      * OLE RPC call is being made to unblock the parent.
      */
     void FlushPendingRPCQueue();
 
 #ifdef OS_WIN
     void ProcessNativeEventsInRPCCall();
+    static void NotifyGeckoEventDispatch();
 
 protected:
     bool WaitForNotify();
     void SpinInternalEventLoop();
 #endif
 
   private:
     // Called on worker thread only
--- a/ipc/glue/SyncChannel.h
+++ b/ipc/glue/SyncChannel.h
@@ -100,16 +100,17 @@ public:
 #ifdef OS_WIN
     struct NS_STACK_CLASS SyncStackFrame
     {
         SyncStackFrame(SyncChannel* channel, bool rpc);
         ~SyncStackFrame();
 
         bool mRPC;
         bool mSpinNestedEvents;
+        bool mListenerNotified;
         SyncChannel* mChannel;
 
         /* the previous stack frame for this channel */
         SyncStackFrame* mPrev;
 
         /* the previous stack frame on any channel */
         SyncStackFrame* mStaticPrev;
     };
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -579,16 +579,17 @@ TimeoutHasExpired(const TimeoutData& aDa
   return now >= aData.targetTicks;
 }
 
 } // anonymous namespace
 
 RPCChannel::SyncStackFrame::SyncStackFrame(SyncChannel* channel, bool rpc)
   : mRPC(rpc)
   , mSpinNestedEvents(false)
+  , mListenerNotified(false)
   , mChannel(channel)
   , mPrev(mChannel->mTopFrame)
   , mStaticPrev(sStaticTopFrame)
 {
   mChannel->mTopFrame = this;
   sStaticTopFrame = this;
 
   if (!mStaticPrev) {
@@ -612,21 +613,39 @@ RPCChannel::SyncStackFrame::~SyncStackFr
     NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
     delete gNeuteredWindows;
     gNeuteredWindows = NULL;
   }
 }
 
 SyncChannel::SyncStackFrame* SyncChannel::sStaticTopFrame;
 
+// nsAppShell's notification that gecko events are being processed.
+// If we are here and there is an RPC Incall active, we are spinning
+// a nested gecko event loop. In which case the remote process needs
+// to know about it.
+void /* static */
+RPCChannel::NotifyGeckoEventDispatch()
+{
+  // sStaticTopFrame is only valid for RPC channels
+  if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
+    return;
+
+  sStaticTopFrame->mListenerNotified = true;
+  RPCChannel* channel = static_cast<RPCChannel*>(sStaticTopFrame->mChannel);
+  channel->Listener()->ProcessRemoteNativeEventsInRPCCall();
+}
+
+// invoked by the module that receives the spin event loop
+// message.
 void
 RPCChannel::ProcessNativeEventsInRPCCall()
 {
   if (!mTopFrame) {
-    NS_ERROR("Child logic error: no RPC frame");
+    NS_ERROR("Spin logic error: no RPC frame");
     return;
   }
 
   mTopFrame->mSpinNestedEvents = true;
 }
 
 // Spin loop is called in place of WaitForNotify when modal ui is being shown
 // in a child. There are some intricacies in using it however. Spin loop is
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2831,18 +2831,17 @@ class _GenerateProtocolActorCode(ipdl.as
         # protocols with union types need Lookup().  we'll give it to
         # all for the time being (simpler)
         if 1 or ptype.isManager():
             self.cls.addstmts(self.implementManagerIface())
 
         # User-facing shmem methods
         self.cls.addstmts(self.makeShmemIface())
 
-        if (ptype.isToplevel() and self.side is 'parent'
-            and ptype.talksRpc()):
+        if (ptype.isToplevel() and ptype.talksRpc()):
 
             processnative = MethodDefn(
                 MethodDecl('ProcessNativeEventsInRPCCall', ret=Type.VOID))
 
             processnative.addstmts([
                     CppDirective('ifdef', 'OS_WIN'),
                     StmtExpr(ExprCall(
                             ExprSelect(p.channelVar(), '.',
--- a/widget/src/windows/nsAppShell.cpp
+++ b/widget/src/windows/nsAppShell.cpp
@@ -33,16 +33,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "mozilla/ipc/RPCChannel.h"
 #include "nsAppShell.h"
 #include "nsToolkit.h"
 #include "nsThreadUtils.h"
 #include "WinTaskbar.h"
 #include "nsString.h"
 #include "nsIMM32Handler.h"
 
 // For skidmark code
@@ -302,16 +303,19 @@ nsAppShell::ProcessNextNativeEvent(PRBoo
 {
 #if defined(_MSC_VER) && defined(_M_IX86)
   if (sXPCOMHasLoadedNewDLLs && sLoadedModules) {
     sXPCOMHasLoadedNewDLLs = PR_FALSE;
     CollectNewLoadedModules();
   }
 #endif
 
+  // Notify ipc we are spinning a (possibly nested) gecko event loop.
+  mozilla::ipc::RPCChannel::NotifyGeckoEventDispatch();
+
   PRBool gotMessage = PR_FALSE;
 
   do {
     MSG msg;
     // Give priority to keyboard and mouse messages.
     if (PeekUIMessage(&msg) ||
         ::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
       gotMessage = PR_TRUE;