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 d40eac0106f5
parent 69668 02f440119508
child 69670 f5ac9367f1e1
push id20061
push userjmathies@mozilla.com
push dateWed, 18 May 2011 11:57:57 +0000
treeherdermozilla-central@d40eac0106f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs648935
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;