Bug 563737 - [QT] Detect when Qt plugins enter a nested event loop. r=cjones
authorTero Koskinen <tero.koskinen@iki.fi>
Tue, 01 Jun 2010 23:58:00 -0400
changeset 43456 a0f157a6cac5db3fe5754191b7cb83c621fae031
parent 43455 a3eb83c7e3864e52c946f892c1d5d7a3a503c072
child 43457 b9f53bbd44de837f78d72566317b17a4c2fa5b75
push id13715
push userCallek@gmail.com
push dateThu, 10 Jun 2010 06:01:34 +0000
treeherdermozilla-central@68ec1fa60b86 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs563737
milestone1.9.3a5pre
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 563737 - [QT] Detect when Qt plugins enter a nested event loop. r=cjones
dom/plugins/Makefile.in
dom/plugins/NestedLoopTimer.cpp
dom/plugins/NestedLoopTimer.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/PluginModuleParent.cpp
--- a/dom/plugins/Makefile.in
+++ b/dom/plugins/Makefile.in
@@ -90,17 +90,29 @@ EXPORTS_mozilla/plugins = \
 
 MODULE           = dom
 LIBRARY_NAME     = domplugins_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 ENABLE_CXX_EXCEPTIONS = 1
 
+ifeq ($(MOZ_ENABLE_QT),1)
+MOCSRCS = \
+  moc_NestedLoopTimer.cpp \
+  $(NULL)
+
+QTSRCS = \
+  NestedLoopTimer.cpp \
+  $(NULL)
+endif
+
 CPPSRCS = \
+  $(MOCSRCS) \
+  $(QTSRCS) \
   ChildAsyncCall.cpp \
   ChildTimer.cpp \
   PluginMessageUtils.cpp \
   PluginInstanceChild.cpp \
   PluginInstanceParent.cpp \
   PluginModuleChild.cpp \
   PluginModuleParent.cpp \
   PluginProcessChild.cpp \
new file mode 100644
--- /dev/null
+++ b/dom/plugins/NestedLoopTimer.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is mozilla.org code.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributors(s):
+ *  Tero Koskinen <tero.koskinen@iki.fi>  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 <QtCore/QTimer>
+
+#include "NestedLoopTimer.h"
+#include "mozilla/plugins/PluginModuleChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+NestedLoopTimer::NestedLoopTimer(PluginModuleChild *pmc):
+     QObject(), mModule(pmc), mQTimer(NULL)
+{
+}
+
+NestedLoopTimer::~NestedLoopTimer()
+{
+    if (mQTimer) {
+        mQTimer->stop();
+        delete mQTimer;
+        mQTimer = NULL;
+    }
+}
+
+void NestedLoopTimer::timeOut()
+{
+    // just detected a nested loop; start a timer that will
+    // periodically rpc-call back into the browser and process some
+    // events
+    mQTimer = new QTimer(this);
+    QObject::connect(mQTimer, SIGNAL(timeout()), this,
+                     SLOT(processSomeEvents()));
+    mQTimer->setInterval(kNestedLoopDetectorIntervalMs);
+    mQTimer->start();
+}
+
+void NestedLoopTimer::processSomeEvents()
+{
+    if (mModule)
+        mModule->CallProcessSomeEvents();
+}
+
+} /* namespace plugins */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/dom/plugins/NestedLoopTimer.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is mozilla.org code.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributors(s):
+ *  Tero Koskinen <tero.koskinen@iki.fi>  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+#ifndef NESTEDLOOPTIMER_H
+#define NESTEDLOOPTIMER_H
+
+#include <QtCore/QObject>
+
+class QTimer;
+
+namespace mozilla {
+namespace plugins {
+
+class PluginModuleChild;
+
+class NestedLoopTimer: public QObject
+{
+    Q_OBJECT
+public:
+    NestedLoopTimer(PluginModuleChild *pmc);
+
+    virtual ~NestedLoopTimer();
+
+public Q_SLOTS:
+    virtual void timeOut();
+    virtual void processSomeEvents();
+   
+private:
+    PluginModuleChild *mModule;
+    QTimer *mQTimer;
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -33,17 +33,19 @@
  * 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 ***** */
 
 #ifdef MOZ_WIDGET_QT
+#include <QtCore/QTimer>
 #include "nsQAppInstance.h"
+#include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleChild.h"
 
 #ifdef MOZ_WIDGET_GTK2
 #include <gtk/gtk.h>
 #endif
 
@@ -91,16 +93,18 @@ static bool gDelayFlashFocusReplyUntilEv
 PluginModuleChild::PluginModuleChild() :
     mLibrary(0),
     mShutdownFunc(0),
     mInitializeFunc(0)
 #if defined(OS_WIN) || defined(OS_MACOSX)
   , mGetEntryPointsFunc(0)
 #elif defined(MOZ_WIDGET_GTK2)
   , mNestedLoopTimerId(0)
+#elif defined(MOZ_WIDGET_QT)
+  , mNestedLoopTimerObject(0)
 #endif
 #ifdef OS_WIN
   , mNestedEventHook(NULL)
   , mGlobalCallWndProcHook(NULL)
 #endif
 {
     NS_ASSERTION(!gInstance, "Something terribly wrong here!");
     memset(&mFunctions, 0, sizeof(mFunctions));
@@ -441,16 +445,36 @@ void
 PluginModuleChild::ExitedCxxStack()
 {
     NS_ABORT_IF_FALSE(0 < mNestedLoopTimerId,
                       "nested loop timeout not scheduled");
 
     g_source_remove(mNestedLoopTimerId);
     mNestedLoopTimerId = 0;
 }
+#elif defined (MOZ_WIDGET_QT)
+
+void
+PluginModuleChild::EnteredCxxStack()
+{
+    NS_ABORT_IF_FALSE(mNestedLoopTimerObject == NULL,
+                      "previous timer not descheduled");
+    mNestedLoopTimerObject = new NestedLoopTimer(this);
+    QTimer::singleShot(kNestedLoopDetectorIntervalMs,
+                       mNestedLoopTimerObject, SLOT(timeOut()));
+}
+
+void
+PluginModuleChild::ExitedCxxStack()
+{
+    NS_ABORT_IF_FALSE(mNestedLoopTimerObject != NULL,
+                      "nested loop timeout not scheduled");
+    delete mNestedLoopTimerObject;
+    mNestedLoopTimerObject = NULL;
+}
 
 #endif
 
 bool
 PluginModuleChild::InitGraphics()
 {
 #if defined(MOZ_WIDGET_GTK2)
     // Work around plugins that don't interact well with GDK
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -84,16 +84,21 @@
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void);
 
 namespace mozilla {
 namespace plugins {
 
+#ifdef MOZ_WIDGET_QT
+class NestedLoopTimer;
+static const int kNestedLoopDetectorIntervalMs = 90;
+#endif
+
 class PluginScriptableObjectChild;
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
 protected:
     NS_OVERRIDE
     virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
@@ -193,16 +198,22 @@ private:
 #if defined(MOZ_WIDGET_GTK2)
     static gboolean DetectNestedEventLoop(gpointer data);
     static gboolean ProcessBrowserEvents(gpointer data);
 
     NS_OVERRIDE
     virtual void EnteredCxxStack();
     NS_OVERRIDE
     virtual void ExitedCxxStack();
+#elif defined(MOZ_WIDGET_QT)
+
+    NS_OVERRIDE
+    virtual void EnteredCxxStack();
+    NS_OVERRIDE
+    virtual void ExitedCxxStack();
 #endif
 
     std::string mPluginFilename;
     PRLibrary* mLibrary;
     nsCString mUserAgent;
 
     // we get this from the plugin
     NP_PLUGINSHUTDOWN mShutdownFunc;
@@ -249,16 +260,18 @@ private:
     guint mNestedLoopTimerId;
 #  ifdef DEBUG
     // Depth of the stack of calls to g_main_context_dispatch before any
     // nested loops are run.  This is 1 when IPC calls are dispatched from
     // g_main_context_iteration, or 0 when dispatched directly from
     // MessagePumpForUI.
     int mTopLoopDepth;
 #  endif
+#elif defined (MOZ_WIDGET_QT)
+    NestedLoopTimer *mNestedLoopTimerObject;
 #endif
 
     struct NPObjectData : public nsPtrHashKey<NPObject>
     {
         NPObjectData(const NPObject* key)
             : nsPtrHashKey<NPObject>(key)
             , instance(NULL)
             , actor(NULL)
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -34,16 +34,20 @@
  * 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 ***** */
 
 #ifdef MOZ_WIDGET_GTK2
 #include <glib.h>
 #endif
+#ifdef MOZ_WIDGET_QT
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEventLoop>
+#endif
 
 #include "base/process_util.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/ipc/SyncChannel.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 #include "PluginIdentifierParent.h"
@@ -749,17 +753,30 @@ PluginModuleParent::AnswerNPN_GetValue_W
                                                       bool* aBoolVal)
 {
     NPBool boolVal = false;
     *aError = mozilla::plugins::parent::_getvalue(nsnull, aVariable, &boolVal);
     *aBoolVal = boolVal ? true : false;
     return true;
 }
 
-#if !defined(MOZ_WIDGET_GTK2)
+#if defined(MOZ_WIDGET_QT)
+static const int kMaxtimeToProcessEvents = 30;
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+    PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
+    QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
+
+    PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
+
+    return true;
+}
+
+#elif !defined(MOZ_WIDGET_GTK2)
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
 {
     NS_RUNTIMEABORT("unreached");
     return false;
 }
 
 #else