Bug 544945, part 2: Periodically unblock the parent to allow it to process events while the plugin subprocess is in a nested event loop. r=karlt
authorChris Jones <jones.chris.g@gmail.com>
Tue, 16 Feb 2010 12:44:25 -0600
changeset 38685 d553de3fad407432815d87555ec5b854454b3657
parent 38684 569dede83071a1f7eb9ce79a1537f6e822ee0f88
child 38686 a31c15677467d51b04a527757bdcdaa2cdcc60c9
push id11800
push userbsmedberg@mozilla.com
push dateThu, 25 Feb 2010 06:51:28 +0000
treeherderautoland@9a4b73f92f0e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs544945
milestone1.9.3a2pre
Bug 544945, part 2: Periodically unblock the parent to allow it to process events while the plugin subprocess is in a nested event loop. r=karlt
dom/plugins/PPluginModule.ipdl
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginModuleParent.h
--- a/dom/plugins/PPluginModule.ipdl
+++ b/dom/plugins/PPluginModule.ipdl
@@ -88,12 +88,18 @@ parent:
     returns (bool aIsString);
 
   sync NPN_GetStringIdentifiers(nsCString[] aNames)
     returns (NPRemoteIdentifier[] aIds);
 
   rpc NPN_GetValue_WithBoolReturn(NPNVariable aVariable)
     returns (NPError aError,
              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();
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -285,17 +285,17 @@ PluginModuleChild::DetectNestedEventLoop
 gboolean
 PluginModuleChild::ProcessBrowserEvents(gpointer data)
 {
     NS_ABORT_IF_FALSE(1 < g_main_depth(),
                       "not canceled before returning to main event loop!");
 
     PluginModuleChild* pmc = static_cast<PluginModuleChild*>(data);
 
-    PLUGIN_LOG_DEBUG(("FIXME/bug 544945: rpc-call to browser to process a few events"));
+    pmc->CallProcessSomeEvents();
 
     return TRUE;
 }
 
 void
 PluginModuleChild::EnteredCxxStack()
 {
     NS_ABORT_IF_FALSE(0 == mNestedLoopTimerId,
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -40,16 +40,17 @@
 
 #include "mozilla/ipc/SyncChannel.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 
 #include "nsContentUtils.h"
 #include "nsCRT.h"
 #include "nsNPAPIPlugin.h"
+#include "nsThreadUtils.h"
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
 using mozilla::ipc::NPRemoteIdentifier;
 using mozilla::ipc::SyncChannel;
 
 using namespace mozilla::plugins;
@@ -832,8 +833,40 @@ PluginModuleParent::AnswerNPN_GetValue_W
                                                       NPError* aError,
                                                       bool* aBoolVal)
 {
     NPBool boolVal = false;
     *aError = mozilla::plugins::parent::_getvalue(nsnull, aVariable, &boolVal);
     *aBoolVal = boolVal ? true : false;
     return true;
 }
+
+#if !defined(MOZ_WIDGET_GTK2)
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+    NS_RUNTIMEABORT("unreached");
+    return false;
+}
+
+#else
+static const int kMaxChancesToProcessEvents = 20;
+
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+    PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
+
+    // XXX it would seem sensical to make the condition be
+    // |NS_HasPendingEvents() && i < kMaxEventsToProcess|.  The
+    // problem is, the native appshell is just an observer of our
+    // nsThread, and processes native events as a side effect of
+    // nsThread::ProcessNextEvent().  Since native events are the ones
+    // we really care about here, we need to go straight to
+    // NS_ProcessNextEvent().
+    for (int i = 0; i < kMaxChancesToProcessEvents; ++i)
+        NS_ProcessNextEvent(nsnull, PR_FALSE);
+
+    PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
+
+    return true;
+}
+#endif
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -149,16 +149,19 @@ protected:
     RecvNPN_GetStringIdentifiers(const nsTArray<nsCString>& aNames,
                                  nsTArray<NPRemoteIdentifier>* aIds);
 
     virtual bool
     AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
                                       NPError* aError,
                                       bool* aBoolVal);
 
+    NS_OVERRIDE
+    virtual bool AnswerProcessSomeEvents();
+
     static PluginInstanceParent* InstCast(NPP instance);
     static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
 
 private:
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     // Implement the module-level functions from NPAPI; these are
     // normally resolved directly from the DSO.