From 395d865b8047f5ac20a082e846e37ceeb1c7afbd Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Tue, 9 Sep 2014 12:23:25 +0200 Subject: Android: fix inputmethod race conditions focusObjectInputMethodQuery() and sendInputMethodEvent() were not thread safe. Remove them, and replace with thread safe versions based on the same principle as queryFocusObjectThreadSafe(). Task-number: QTBUG-40995 Change-Id: Idb6f0c6d3963b7e8e73e029e83d0367088146ca8 Reviewed-by: Christian Stromme Reviewed-by: BogDan Vatra --- .../platforms/android/qandroidinputcontext.cpp | 114 ++++++++++----------- .../platforms/android/qandroidinputcontext.h | 11 +- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 90eff615a2..36d73b2971 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -464,7 +464,7 @@ void QAndroidInputContext::commit() void QAndroidInputContext::updateCursorPosition() { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) { const int cursorPos = getAbsoluteCursorPosition(query); const int composeLength = m_composingText.length(); @@ -497,7 +497,7 @@ void QAndroidInputContext::updateCursorPosition() void QAndroidInputContext::update(Qt::InputMethodQueries queries) { - QSharedPointer query = focusObjectInputMethodQuery(queries); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(queries); if (query.isNull()) return; #warning TODO extract the needed data from query @@ -525,7 +525,7 @@ bool QAndroidInputContext::isAnimating() const void QAndroidInputContext::showInputPanel() { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return; @@ -581,16 +581,6 @@ void QAndroidInputContext::setFocusObject(QObject *object) QPlatformInputContext::setFocusObject(object); } -void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodEvent *event) -{ - QCoreApplication::sendEvent(receiver, event); -} - -void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodQueryEvent *event) -{ - QCoreApplication::sendEvent(receiver, event); -} - jboolean QAndroidInputContext::beginBatchEdit() { ++m_batchEditNestingLevel; @@ -616,12 +606,12 @@ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPos { QInputMethodEvent event; event.setCommitString(text); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); clear(); // Qt has now put the cursor at the end of the text, corresponding to newCursorPosition == 1 if (newCursorPosition != 1) { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (!query.isNull()) { QList attributes; const int localPos = query->value(Qt::ImCursorPosition).toInt(); @@ -640,7 +630,7 @@ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPos jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength) { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return JNI_TRUE; @@ -649,7 +639,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right QInputMethodEvent event; event.setCommitString(QString(), -leftLength, leftLength+rightLength); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); clear(); return JNI_TRUE; @@ -658,7 +648,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right // Android docs say the cursor must not move jboolean QAndroidInputContext::finishComposingText() { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return JNI_FALSE; @@ -674,7 +664,7 @@ jboolean QAndroidInputContext::finishComposingText() QInputMethodEvent event(QString(), attributes); event.setCommitString(m_composingText); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); clear(); return JNI_TRUE; @@ -683,7 +673,7 @@ jboolean QAndroidInputContext::finishComposingText() jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) { jint res = 0; - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return res; @@ -706,7 +696,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex // updateExtractedText(View, int, ExtractedText) whenever you call // updateSelection(View, int, int, int, int)." QTBUG-37980 - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return m_extractedText; @@ -751,7 +741,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex QString QAndroidInputContext::getSelectedText(jint /*flags*/) { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return QString(); @@ -767,7 +757,7 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) } //compatibility code for old controls that do not implement the new API - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return QString(); @@ -787,7 +777,7 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) } //compatibility code for old controls that do not implement the new API - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return QString(); @@ -813,7 +803,7 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return JNI_FALSE; @@ -836,7 +826,7 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur QVariant(underlined))); QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); updateCursorPosition(); @@ -858,7 +848,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) if (wasComposing) finishComposingText(); - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return JNI_FALSE; @@ -904,7 +894,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) QInputMethodEvent event(m_composingText, attributes); event.setCommitString(QString(), relativeStart, length); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); m_blockUpdateSelection = updateSelectionWasBlocked; @@ -921,7 +911,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) jboolean QAndroidInputContext::setSelection(jint start, jint end) { - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQueryThreadSafe(); if (query.isNull()) return JNI_FALSE; @@ -953,7 +943,7 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end) QVariant())); } QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEvent(&event); + sendInputMethodEventThreadSafe(&event); updateCursorPosition(); return JNI_TRUE; } @@ -996,8 +986,10 @@ Q_INVOKABLE QVariant QAndroidInputContext::queryFocusObjectUnsafe(Qt::InputMetho QVariant QAndroidInputContext::queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument) { - const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); QVariant retval; + if (!qGuiApp) + return retval; + const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) return retval; @@ -1010,46 +1002,54 @@ QVariant QAndroidInputContext::queryFocusObjectThreadSafe(Qt::InputMethodQuery q return retval; } -QSharedPointer QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) -{ -#warning TODO make qGuiApp->focusObject() thread safe !!! +QSharedPointer QAndroidInputContext::focusObjectInputMethodQueryThreadSafe(Qt::InputMethodQueries queries) { + QSharedPointer retval; + if (!qGuiApp) + return QSharedPointer(); const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) return QSharedPointer(); + + QInputMethodQueryEvent *queryEvent = 0; + QMetaObject::invokeMethod(this, "focusObjectInputMethodQueryUnsafe", + inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QInputMethodQueryEvent*, queryEvent), + Q_ARG(Qt::InputMethodQueries, queries)); + + return QSharedPointer(queryEvent); +} + +QInputMethodQueryEvent *QAndroidInputContext::focusObjectInputMethodQueryUnsafe(Qt::InputMethodQueries queries) +{ QObject *focusObject = qGuiApp->focusObject(); if (!focusObject) - return QSharedPointer(); - - QSharedPointer ret = QSharedPointer(new QInputMethodQueryEvent(queries)); - if (inMainThread) { - QCoreApplication::sendEvent(focusObject, ret.data()); - } else { - QMetaObject::invokeMethod(this, - "sendEvent", - Qt::BlockingQueuedConnection, - Q_ARG(QObject*, focusObject), - Q_ARG(QInputMethodQueryEvent*, ret.data())); - } + return 0; + QInputMethodQueryEvent *ret = new QInputMethodQueryEvent(queries); + QCoreApplication::sendEvent(focusObject, ret); return ret; } -void QAndroidInputContext::sendInputMethodEvent(QInputMethodEvent *event) +void QAndroidInputContext::sendInputMethodEventUnsafe(QInputMethodEvent *event) { -#warning TODO make qGuiApp->focusObject() thread safe !!! QObject *focusObject = qGuiApp->focusObject(); if (!focusObject) return; - if (qGuiApp->thread() == QThread::currentThread()) { - QCoreApplication::sendEvent(focusObject, event); - } else { - QMetaObject::invokeMethod(this, - "sendEvent", - Qt::BlockingQueuedConnection, - Q_ARG(QObject*, focusObject), - Q_ARG(QInputMethodEvent*, event)); - } + QCoreApplication::sendEvent(focusObject, event); +} + +void QAndroidInputContext::sendInputMethodEventThreadSafe(QInputMethodEvent *event) +{ + if (!qGuiApp) + return; + const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); + if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) + return; + + QMetaObject::invokeMethod(this, "sendInputMethodEventUnsafe", + inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection, + Q_ARG(QInputMethodEvent*, event)); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index a467e4849e..ffadacaad4 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -121,16 +121,15 @@ public slots: void updateCursorPosition(); private: - QSharedPointer focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); - void sendInputMethodEvent(QInputMethodEvent *event); + void sendInputMethodEventThreadSafe(QInputMethodEvent *event); + Q_INVOKABLE void sendInputMethodEventUnsafe(QInputMethodEvent *event); + + QSharedPointer focusObjectInputMethodQueryThreadSafe(Qt::InputMethodQueries queries = Qt::ImQueryAll); + Q_INVOKABLE QInputMethodQueryEvent *focusObjectInputMethodQueryUnsafe(Qt::InputMethodQueries queries); Q_INVOKABLE QVariant queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument); QVariant queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument); -private slots: - virtual void sendEvent(QObject *receiver, QInputMethodEvent *event); - virtual void sendEvent(QObject *receiver, QInputMethodQueryEvent *event); - private: ExtractedText m_extractedText; QString m_composingText; -- cgit v1.2.3