Bug 590299 - Virtual keyboard is not invoked for input fields in Qt plugins r=karlt,jbos
authorOleg Romashin <romaxa@gmail.com>
Wed, 07 Sep 2011 11:06:35 -0400
changeset 76675 789165341d81e48c02fa7092a180fdca25e2a7df
parent 76674 754865ae97bcbc0d7249aca76ceef93f2354efc2
child 76676 fd50c3e8aad3b007cc088f029a86db33f48365df
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewerskarlt, jbos
bugs590299
milestone9.0a1
Bug 590299 - Virtual keyboard is not invoked for input fields in Qt plugins r=karlt,jbos
widget/src/qt/mozqwidget.cpp
widget/src/qt/mozqwidget.h
widget/src/qt/nsWindow.cpp
widget/src/qt/nsWindow.h
--- a/widget/src/qt/mozqwidget.cpp
+++ b/widget/src/qt/mozqwidget.cpp
@@ -159,17 +159,17 @@ void MozQWidget::dropEvent(QGraphicsScen
 void MozQWidget::focusInEvent(QFocusEvent* aEvent)
 {
     mReceiver->OnFocusInEvent(aEvent);
 
     // The application requested the VKB during startup but did not manage
     // to open it, because there was no focused window yet so we do it now by
     // requesting the VKB without any timeout.
     if (gFailedOpenKeyboard)
-        requestVKB(0);
+        requestVKB(0, this);
 }
 
 #ifdef MOZ_ENABLE_QTMOBILITY
 void MozQWidget::orientationChanged()
 {
     if (!scene() || !scene()->views().size()) {
         return;
     }
@@ -561,35 +561,37 @@ QVariant MozQWidget::inputMethodQuery(Qt
     return QVariant();
 }
 
 /**
   Request the VKB and starts a timer with the given timeout in milliseconds.
   If the request is not canceled when the timer runs out, the VKB is actually
   shown.
 */
-void MozQWidget::requestVKB(int aTimeout)
+void MozQWidget::requestVKB(int aTimeout, QObject* aWidget)
 {
     if (!gPendingVKBOpen) {
         gPendingVKBOpen = true;
 
-        if (aTimeout == 0)
+        if (aTimeout == 0 || !aWidget) {
             showVKB();
-        else
-            QTimer::singleShot(aTimeout, this, SLOT(showVKB()));
+        } else {
+            QTimer::singleShot(aTimeout, aWidget, SLOT(showVKB()));
+        }
     }
 }
 
 
 
 void MozQWidget::showVKB()
 {
     // skip showing of keyboard if not pending
-    if (!gPendingVKBOpen)
+    if (!gPendingVKBOpen) {
         return;
+    }
 
     gPendingVKBOpen = false;
 
 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
     QWidget* focusWidget = qApp->focusWidget();
 
     if (focusWidget) {
         QInputContext *inputContext = qApp->inputContext();
--- a/widget/src/qt/mozqwidget.h
+++ b/widget/src/qt/mozqwidget.h
@@ -86,22 +86,22 @@ public:
     void activate();
     void deactivate();
 
     QVariant inputMethodQuery(Qt::InputMethodQuery aQuery) const;
 
     /**
      * VirtualKeyboardIntegration
      */
-    void requestVKB(int aTimeout);
-    void hideVKB();
-    bool isVKBOpen();
+    static void requestVKB(int aTimeout = 0, QObject* aWidget = 0);
+    static void hideVKB();
+    static bool isVKBOpen();
 
 public slots:
-    void showVKB();
+    static void showVKB();
 
 #ifdef MOZ_ENABLE_QTMOBILITY
     void orientationChanged();
 #endif
 
 protected:
     virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent* aEvent);
     virtual void dragEnterEvent(QGraphicsSceneDragDropEvent* aEvent);
--- a/widget/src/qt/nsWindow.cpp
+++ b/widget/src/qt/nsWindow.cpp
@@ -124,16 +124,21 @@ using namespace QtMobility;
 #if MOZ_PLATFORM_MAEMO > 5
 #include "nsIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsIFocusManager.h"
 #endif
 
 #ifdef MOZ_X11
 #include "keysym2ucs.h"
+#if MOZ_PLATFORM_MAEMO == 6
+#include <X11/Xatom.h>
+static Atom sPluginIMEAtom;
+#define PLUGIN_VKB_REQUEST_PROP "_NPAPI_PLUGIN_REQUEST_VKB"
+#endif
 #endif //MOZ_X11
 
 #include <QtOpenGL/QGLWidget>
 #include <QtOpenGL/QGLContext>
 #define GLdouble_defined 1
 #include "Layers.h"
 #include "LayerManagerOGL.h"
 
@@ -243,16 +248,19 @@ nsWindow::nsWindow()
     mGesturesCancelled   = PR_FALSE;
     mTimerStarted        = PR_FALSE;
     mPinchEvent.needDispatch = false;
     mMoveEvent.needDispatch = false;
     
     if (!gGlobalsInitialized) {
         gGlobalsInitialized = PR_TRUE;
 
+#if defined(MOZ_X11) && (MOZ_PLATFORM_MAEMO == 6)
+        sPluginIMEAtom = XInternAtom(QX11Info::display(), PLUGIN_VKB_REQUEST_PROP, False);
+#endif
         // It's OK if either of these fail, but it may not be one day.
         initialize_prefs();
     }
 
     memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags));
 
     mIsTransparent = PR_FALSE;
 
@@ -3113,63 +3121,183 @@ PRBool
 nsWindow::AreBoundsSane(void)
 {
     if (mBounds.width > 0 && mBounds.height > 0)
         return PR_TRUE;
 
     return PR_FALSE;
 }
 
+#if defined(MOZ_X11) && (MOZ_PLATFORM_MAEMO == 6)
+typedef enum {
+    VKBUndefined,
+    VKBOpen,
+    VKBClose
+} PluginVKBState;
+
+static QCoreApplication::EventFilter previousEventFilter = NULL;
+
+static PluginVKBState
+GetPluginVKBState(Window aWinId)
+{
+    // Set default value as unexpected error
+    PluginVKBState imeState = VKBUndefined;
+    Display *display = QX11Info::display();
+
+    Atom actualType;
+    int actualFormat;
+    unsigned long nitems;
+    unsigned long bytes;
+    union {
+        unsigned char* asUChar;
+        unsigned long* asLong;
+    } data = {0};
+    int status = XGetWindowProperty(display, aWinId, sPluginIMEAtom,
+                                    0, 1, False, AnyPropertyType,
+                                    &actualType, &actualFormat, &nitems,
+                                    &bytes, &data.asUChar);
+
+    if (status == Success && actualType == XA_CARDINAL && actualFormat == 32 && nitems == 1) {
+        // Assume that plugin set value false - close VKB, true - open VKB
+        imeState = data.asLong[0] ? VKBOpen : VKBClose;
+    }
+
+    if (status == Success) {
+        XFree(data.asUChar);
+    }
+
+    return imeState;
+}
+
+static void
+SetVKBState(Window aWinId, PluginVKBState aState)
+{
+    Display *display = QX11Info::display();
+    if (aState != VKBUndefined) {
+        unsigned long isOpen = aState == VKBOpen ? 1 : 0;
+        XChangeProperty(display, aWinId, sPluginIMEAtom, XA_CARDINAL, 32,
+                        PropModeReplace, (unsigned char *) &isOpen, 1);
+    } else {
+        XDeleteProperty(display, aWinId, sPluginIMEAtom);
+    }
+    XSync(display, False);
+}
+
+static bool
+x11EventFilter(void* message, long* result)
+{
+    XEvent* event = static_cast<XEvent*>(message);
+    if (event->type == PropertyNotify) {
+        if (event->xproperty.atom == sPluginIMEAtom) {
+            PluginVKBState state = GetPluginVKBState(event->xproperty.window);
+            if (state == VKBOpen) {
+                MozQWidget::requestVKB();
+            } else if (state == VKBClose) {
+                MozQWidget::hideVKB();
+            }
+            return true;
+        }
+    }
+    if (previousEventFilter) {
+        return previousEventFilter(message, result);
+    }
+
+    return false;
+}
+#endif
+
 NS_IMETHODIMP
 nsWindow::SetInputMode(const IMEContext& aContext)
 {
     NS_ENSURE_TRUE(mWidget, NS_ERROR_FAILURE);
 
+    // SetSoftwareKeyboardState uses mIMEContext,
+    // so, before calling that, record aContext in mIMEContext.
     mIMEContext = aContext;
 
-     // Ensure that opening the virtual keyboard is allowed for this specific
-     // IMEContext depending on the content.ime.strict.policy pref
-     if (aContext.mStatus != nsIWidget::IME_STATUS_DISABLED && 
-         aContext.mStatus != nsIWidget::IME_STATUS_PLUGIN) {
-       if (Preferences::GetBool("content.ime.strict_policy", PR_FALSE) &&
-           !aContext.FocusMovedByUser() &&
-           aContext.FocusMovedInContentProcess()) {
-         return NS_OK;
-       }
-     }
-
-    switch (aContext.mStatus) {
+#if defined(MOZ_X11) && (MOZ_PLATFORM_MAEMO == 6)
+    static QCoreApplication::EventFilter currentEventFilter = NULL;
+    if (mIMEContext.mStatus == nsIWidget::IME_STATUS_PLUGIN && currentEventFilter != x11EventFilter) {
+        // Install event filter for listening Plugin IME state changes
+        previousEventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
+        currentEventFilter = x11EventFilter;
+    } else if (mIMEContext.mStatus != nsIWidget::IME_STATUS_PLUGIN && currentEventFilter == x11EventFilter) {
+        // Remove event filter
+        QCoreApplication::instance()->setEventFilter(previousEventFilter);
+        currentEventFilter = previousEventFilter;
+        previousEventFilter = NULL;
+        QWidget* view = GetViewWidget();
+        if (view) {
+            SetVKBState(view->winId(), VKBUndefined);
+        }
+    }
+#endif
+
+    switch (mIMEContext.mStatus) {
         case nsIWidget::IME_STATUS_ENABLED:
         case nsIWidget::IME_STATUS_PASSWORD:
         case nsIWidget::IME_STATUS_PLUGIN:
-            {
-                PRInt32 openDelay =
-                    Preferences::GetInt("ui.vkb.open.delay", 200);
-                mWidget->requestVKB(openDelay);
-            }
+            SetSoftwareKeyboardState(PR_TRUE);
             break;
         default:
-            mWidget->hideVKB();
+            SetSoftwareKeyboardState(PR_FALSE);
             break;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::GetInputMode(IMEContext& aContext)
 {
     aContext = mIMEContext;
     return NS_OK;
 }
 
 void
+nsWindow::SetSoftwareKeyboardState(PRBool aOpen)
+{
+    if (aOpen) {
+        NS_ENSURE_TRUE(mIMEContext.mStatus != nsIWidget::IME_STATUS_DISABLED,);
+
+        // Ensure that opening the virtual keyboard is allowed for this specific
+        // IMEContext depending on the content.ime.strict.policy pref
+        if (mIMEContext.mStatus != nsIWidget::IME_STATUS_PLUGIN) {
+            if (Preferences::GetBool("content.ime.strict_policy", PR_FALSE) &&
+                !mIMEContext.FocusMovedByUser() &&
+                mIMEContext.FocusMovedInContentProcess()) {
+                return;
+            }
+        }
+#if defined(MOZ_X11) && (MOZ_PLATFORM_MAEMO == 6)
+        // doen't open VKB if plugin did set closed state
+        else {
+            QWidget* view = GetViewWidget();
+            if (view && GetPluginVKBState(view->winId()) == VKBClose) {
+                return;
+            }
+        }
+#endif
+    }
+
+    if (aOpen) {
+        // VKB open need to be delayed in order to give
+        // to plugins chance prevent VKB from opening
+        PRInt32 openDelay =
+            Preferences::GetInt("ui.vkb.open.delay", 200);
+        MozQWidget::requestVKB(openDelay, mWidget);
+    } else {
+        MozQWidget::hideVKB();
+    }
+    return;
+}
+
+void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   if (mIdleService) {
     mIdleService->ResetIdleTimeOut();
   }
 }
-
--- a/widget/src/qt/nsWindow.h
+++ b/widget/src/qt/nsWindow.h
@@ -343,16 +343,17 @@ private:
 
     void*              SetupPluginPort(void);
     nsresult           SetWindowIconList(const nsTArray<nsCString> &aIconList);
     void               SetDefaultIcon(void);
     void               InitButtonEvent(nsMouseEvent &event, QGraphicsSceneMouseEvent *aEvent, int aClickCount = 1);
     nsEventStatus      DispatchCommandEvent(nsIAtom* aCommand);
     nsEventStatus      DispatchContentCommandEvent(PRInt32 aMsg);
     MozQWidget*        createQWidget(MozQWidget *parent, nsWidgetInitData *aInitData);
+    void               SetSoftwareKeyboardState(PRBool aOpen);
 
     MozQWidget*        mWidget;
 
     PRUint32           mIsVisible : 1,
                        mActivatePending : 1;
     PRInt32            mSizeState;
     PluginType         mPluginType;