diff options
author | Peter Varga <pvarga@inf.u-szeged.hu> | 2020-01-15 10:47:37 +0100 |
---|---|---|
committer | Peter Varga <pvarga@inf.u-szeged.hu> | 2020-02-17 23:25:11 +0000 |
commit | 2d7a173b1f00c108cdf2ab451ff7f9af0094c2fa (patch) | |
tree | f2126b080c08b3f249105835d6a111092bb4e1e4 | |
parent | 2cbd4ba7703756c3b0b5b37b118e755e5ea0bfe6 (diff) |
Fix quick accessibility on macOS
Same as the widget fix:
ffdf7ece Fix widget accessibility on macOS
This patch depends on a focusChild() fix in qtdeclarative:
6420ad91d3 Fix QAccessibleQuickWindow::focusChild() to return focused descendant
Task-number: QTBUG-78284
Task-number: QTBUG-81539
Change-Id: If0da937d2c778a158ce02e1433b28ca0888692d8
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
8 files changed, 178 insertions, 13 deletions
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 83ada3c11..3ce202695 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -747,26 +747,34 @@ QQuickWebEngineViewAccessible::QQuickWebEngineViewAccessible(QQuickWebEngineView QAccessibleInterface *QQuickWebEngineViewAccessible::parent() const { QQuickItem *parent = engineView()->parentItem(); - return QAccessible::queryAccessibleInterface(parent); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(parent); + if (!iface) + return QAccessible::queryAccessibleInterface(engineView()->window()); + return iface; +} + +QAccessibleInterface *QQuickWebEngineViewAccessible::focusChild() const +{ + if (child(0) && child(0)->focusChild()) + return child(0)->focusChild(); + return const_cast<QQuickWebEngineViewAccessible *>(this); } int QQuickWebEngineViewAccessible::childCount() const { - if (engineView() && child(0)) - return 1; - return 0; + return child(0) ? 1 : 0; } QAccessibleInterface *QQuickWebEngineViewAccessible::child(int index) const { - if (index == 0) + if (index == 0 && engineView()) return engineView()->d_func()->adapter->browserAccessible(); return 0; } int QQuickWebEngineViewAccessible::indexOfChild(const QAccessibleInterface *c) const { - if (c == child(0)) + if (child(0) && c == child(0)) return 0; return -1; } @@ -778,7 +786,7 @@ QString QQuickWebEngineViewAccessible::text(QAccessible::Text) const QAccessible::Role QQuickWebEngineViewAccessible::role() const { - return QAccessible::Document; + return QAccessible::Client; } QAccessible::State QQuickWebEngineViewAccessible::state() const @@ -939,10 +947,17 @@ void QQuickWebEngineViewPrivate::widgetChanged(RenderWidgetHostViewQtDelegateQui { Q_Q(QQuickWebEngineView); - if (oldWidget) + if (oldWidget) { oldWidget->setParentItem(nullptr); +#if QT_CONFIG(accessibility) + QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(QAccessible::queryAccessibleInterface(oldWidget))); +#endif + } if (newWidget) { +#if QT_CONFIG(accessibility) + QAccessible::registerAccessibleInterface(new QtWebEngineCore::RenderWidgetHostViewQtDelegateQuickAccessible(newWidget, q)); +#endif newWidget->setParentItem(q); newWidget->setSize(q->boundingRect().size()); // Focus on creation if the view accepts it diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index a2ae86f91..6408767b3 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -235,6 +235,7 @@ class QQuickWebEngineViewAccessible : public QAccessibleObject public: QQuickWebEngineViewAccessible(QQuickWebEngineView *o); QAccessibleInterface *parent() const override; + QAccessibleInterface *focusChild() const override; int childCount() const override; QAccessibleInterface *child(int index) const override; int indexOfChild(const QAccessibleInterface*) const override; diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp index ac32671aa..ede85ca69 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp @@ -373,4 +373,53 @@ bool RenderWidgetHostViewQtDelegateQuick::copySurface(const QRect &rect, const Q return true; } +#if QT_CONFIG(accessibility) +RenderWidgetHostViewQtDelegateQuickAccessible::RenderWidgetHostViewQtDelegateQuickAccessible(RenderWidgetHostViewQtDelegateQuick *o, QQuickWebEngineView *view) + : QAccessibleObject(o) + , m_view(view) +{ +} + +QAccessibleInterface *RenderWidgetHostViewQtDelegateQuickAccessible::parent() const +{ + return QAccessible::queryAccessibleInterface(m_view)->parent(); +} + +QString RenderWidgetHostViewQtDelegateQuickAccessible::text(QAccessible::Text) const +{ + return QString(); +} + +QAccessible::Role RenderWidgetHostViewQtDelegateQuickAccessible::role() const +{ + return QAccessible::Client; +} + +QAccessible::State RenderWidgetHostViewQtDelegateQuickAccessible::state() const +{ + return QAccessible::queryAccessibleInterface(m_view)->state(); +} + +QAccessibleInterface *RenderWidgetHostViewQtDelegateQuickAccessible::focusChild() const +{ + return QAccessible::queryAccessibleInterface(m_view)->focusChild(); +} + +int RenderWidgetHostViewQtDelegateQuickAccessible::childCount() const +{ + return QAccessible::queryAccessibleInterface(m_view)->childCount(); +} + +QAccessibleInterface *RenderWidgetHostViewQtDelegateQuickAccessible::child(int index) const +{ + return QAccessible::queryAccessibleInterface(m_view)->child(index); +} + +int RenderWidgetHostViewQtDelegateQuickAccessible::indexOfChild(const QAccessibleInterface *c) const +{ + return QAccessible::queryAccessibleInterface(m_view)->indexOfChild(c); +} + +#endif // QT_CONFIG(accessibility) + } // namespace QtWebEngineCore diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h index b55b2d658..6874aac2b 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h @@ -42,6 +42,7 @@ #include "render_widget_host_view_qt_delegate.h" +#include <QAccessibleObject> #include <QQuickItem> QT_BEGIN_NAMESPACE @@ -115,6 +116,27 @@ private: QQuickWebEngineView *m_view = nullptr; }; +#if QT_CONFIG(accessibility) +class RenderWidgetHostViewQtDelegateQuickAccessible : public QAccessibleObject +{ +public: + RenderWidgetHostViewQtDelegateQuickAccessible(RenderWidgetHostViewQtDelegateQuick *o, QQuickWebEngineView *view); + + QAccessibleInterface *parent() const override; + QString text(QAccessible::Text t) const override; + QAccessible::Role role() const override; + QAccessible::State state() const override; + + QAccessibleInterface *focusChild() const override; + int childCount() const override; + QAccessibleInterface *child(int index) const override; + int indexOfChild(const QAccessibleInterface *) const override; + +private: + QQuickWebEngineView *m_view; +}; +#endif // QT_CONFIG(accessibility) + } // namespace QtWebEngineCore #endif diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index 82ea3be37..8e802a836 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -230,6 +230,7 @@ void tst_Dialogs::javaScriptDialogRequested() QTRY_VERIFY(m_listner->ready()); // make sure javascript executes no longer } +static QByteArrayList params; +W_QTEST_MAIN(tst_Dialogs, params) #include "tst_dialogs.moc" -W_QTEST_MAIN(tst_Dialogs) diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 5572515a1..6f9cb9c69 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -92,6 +92,8 @@ private Q_SLOTS: void javascriptClipboard_data(); void javascriptClipboard(); void setProfile(); + void focusChild(); + void focusChild_data(); private: inline QQuickWebEngineView *newWebEngineView(); @@ -1162,5 +1164,71 @@ void tst_QQuickWebEngineView::setProfile() { QTRY_COMPARE(webEngineView()->url() ,urlFromTestPath("html/basic_page2.html")); } -QTEST_MAIN(tst_QQuickWebEngineView) +void tst_QQuickWebEngineView::focusChild_data() +{ + QTest::addColumn<QString>("interfaceName"); + QTest::addColumn<QVector<QAccessible::Role>>("ancestorRoles"); + + QTest::newRow("QQuickWebEngineView") << QString("QQuickWebEngineView") << QVector<QAccessible::Role>({QAccessible::Client}); + QTest::newRow("RenderWidgetHostViewQtDelegate") << QString("RenderWidgetHostViewQtDelegate") << QVector<QAccessible::Role>({QAccessible::Client}); + QTest::newRow("QQuickView") << QString("QQuickView") << QVector<QAccessible::Role>({QAccessible::Window, QAccessible::Client /* view */}); +} + +void tst_QQuickWebEngineView::focusChild() +{ + auto traverseToWebDocumentAccessibleInterface = [](QAccessibleInterface *iface) -> QAccessibleInterface * { + QFETCH(QVector<QAccessible::Role>, ancestorRoles); + for (int i = 0; i < ancestorRoles.size(); ++i) { + if (iface->childCount() == 0 || iface->role() != ancestorRoles[i]) + return nullptr; + iface = iface->child(0); + } + + if (iface->role() != QAccessible::WebDocument) + return nullptr; + + return iface; + }; + + QQuickWebEngineView *view = webEngineView(); + m_window->show(); + view->settings()->setFocusOnNavigationEnabled(true); + view->setSize(QSizeF(640, 480)); + view->loadHtml("<html><body>" + "<input id='input1' type='text'>" + "</body></html>"); + QVERIFY(waitForLoadSucceeded(view)); + + QAccessibleInterface *iface = nullptr; + QFETCH(QString, interfaceName); + if (interfaceName == "QQuickWebEngineView") + iface = QAccessible::queryAccessibleInterface(view); + else if (interfaceName == "RenderWidgetHostViewQtDelegate") + iface = QAccessible::queryAccessibleInterface(m_window->focusObject()); + else if (interfaceName == "QQuickView") + iface = QAccessible::queryAccessibleInterface(m_window.data()); + QVERIFY(iface); + + // Make sure the input field does not have the focus. + runJavaScript("document.getElementById('input1').blur();"); + QTRY_VERIFY(evaluateJavaScriptSync(view, "document.activeElement.id").toString().isEmpty()); + + QVERIFY(iface->focusChild()); + QTRY_COMPARE(iface->focusChild()->role(), QAccessible::WebDocument); + QCOMPARE(traverseToWebDocumentAccessibleInterface(iface), iface->focusChild()); + + // Set active focus on the input field. + runJavaScript("document.getElementById('input1').focus();"); + QTRY_COMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1")); + + QVERIFY(iface->focusChild()); + QTRY_COMPARE(iface->focusChild()->role(), QAccessible::EditableText); + // <html> -> <body> -> <input> + QCOMPARE(traverseToWebDocumentAccessibleInterface(iface)->child(0)->child(0), iface->focusChild()); +} + +static QByteArrayList params = QByteArrayList() + << "--force-renderer-accessibility"; + +W_QTEST_MAIN(tst_QQuickWebEngineView, params) #include "tst_qquickwebengineview.moc" diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp index c9abe9cfe..518ddaa0d 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp +++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp @@ -157,5 +157,6 @@ void tst_QQuickWebEngineViewGraphics::setHtml(const QString &html) QTRY_COMPARE_WITH_TIMEOUT(m_view->rootObject()->property("loading"), QVariant(false), 30000); } -W_QTEST_MAIN(tst_QQuickWebEngineViewGraphics) +static QByteArrayList params; +W_QTEST_MAIN(tst_QQuickWebEngineViewGraphics, params) #include "tst_qquickwebengineviewgraphics.moc" diff --git a/tests/auto/quick/shared/util.h b/tests/auto/quick/shared/util.h index fbce8bfa7..b7b7b1564 100644 --- a/tests/auto/quick/shared/util.h +++ b/tests/auto/quick/shared/util.h @@ -168,11 +168,19 @@ inline QString activeElementId(QQuickWebEngineView *webEngineView) return arguments.at(1).toString(); } -#define W_QTEST_MAIN(TestObject) \ +#define W_QTEST_MAIN(TestObject, params) \ int main(int argc, char *argv[]) \ { \ QtWebEngine::initialize(); \ - QGuiApplication app(argc, argv); \ + \ + QVector<const char *> w_argv(argc); \ + for (int i = 0; i < argc; ++i) \ + w_argv[i] = argv[i]; \ + for (int i = 0; i < params.size(); ++i) \ + w_argv.append(params[i].data()); \ + int w_argc = w_argv.size(); \ + \ + QGuiApplication app(w_argc, const_cast<char **>(w_argv.data())); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ TestObject tc; \ QTEST_SET_MAIN_SOURCE_PATH \ |