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 <christian.stromme@digia.com>
Reviewed-by: BogDan Vatra <bogdan@kde.org>
bb10
Paul Olav Tvete 2014-09-09 12:23:25 +02:00 committed by BogDan Vatra
parent b678cc2989
commit 395d865b80
2 changed files with 62 additions and 63 deletions

View File

@ -464,7 +464,7 @@ void QAndroidInputContext::commit()
void QAndroidInputContext::updateCursorPosition()
{
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries);
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe();
if (!query.isNull()) {
QList<QInputMethodEvent::Attribute> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe();
if (query.isNull())
return m_extractedText;
@ -751,7 +741,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex
QString QAndroidInputContext::getSelectedText(jint /*flags*/)
{
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
QSharedPointer<QInputMethodQueryEvent> 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<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries)
{
#warning TODO make qGuiApp->focusObject() thread safe !!!
QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQueryThreadSafe(Qt::InputMethodQueries queries) {
QSharedPointer<QInputMethodQueryEvent> retval;
if (!qGuiApp)
return QSharedPointer<QInputMethodQueryEvent>();
const bool inMainThread = qGuiApp->thread() == QThread::currentThread();
if (QAndroidEventDispatcherStopper::stopped() && !inMainThread)
return QSharedPointer<QInputMethodQueryEvent>();
QInputMethodQueryEvent *queryEvent = 0;
QMetaObject::invokeMethod(this, "focusObjectInputMethodQueryUnsafe",
inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QInputMethodQueryEvent*, queryEvent),
Q_ARG(Qt::InputMethodQueries, queries));
return QSharedPointer<QInputMethodQueryEvent>(queryEvent);
}
QInputMethodQueryEvent *QAndroidInputContext::focusObjectInputMethodQueryUnsafe(Qt::InputMethodQueries queries)
{
QObject *focusObject = qGuiApp->focusObject();
if (!focusObject)
return QSharedPointer<QInputMethodQueryEvent>();
QSharedPointer<QInputMethodQueryEvent> ret = QSharedPointer<QInputMethodQueryEvent>(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

View File

@ -121,16 +121,15 @@ public slots:
void updateCursorPosition();
private:
QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
void sendInputMethodEvent(QInputMethodEvent *event);
void sendInputMethodEventThreadSafe(QInputMethodEvent *event);
Q_INVOKABLE void sendInputMethodEventUnsafe(QInputMethodEvent *event);
QSharedPointer<QInputMethodQueryEvent> 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;