From 4c305cba0bf7cc021fd355af574b431f0d5a057d Mon Sep 17 00:00:00 2001
From: Alexandru Croitor <alexandru.croitor@theqtcompany.com>
Date: Mon, 9 May 2016 09:26:27 +0200
Subject: [PATCH] Disabled WebEngine views should not receive focus.

Currently if a QWebEngineView or a QQuickWebEngineView is disabled
using setEnabled(false), after loading a web page, the views are
automatically focused, and a user might see a blinking caret in an html
input for example, even though the user can't interact with it.

Fix consists in not calling the Focus() method whenever a view is
disabled.

Change-Id: I1014fb5898a5ddf01a4e9b14c3eaf5d4006e5131
Task-number: QTBUG-53159
Reviewed-by: Michal Klocek <michal.klocek@theqtcompany.com>
---
 src/core/web_contents_adapter_client.h        |  1 +
 src/core/web_contents_view_qt.cpp             |  2 ++
 src/webengine/api/qquickwebengineview.cpp     |  6 ++++
 src/webengine/api/qquickwebengineview_p_p.h   |  1 +
 src/webenginewidgets/api/qwebenginepage.cpp   |  9 ++++++
 src/webenginewidgets/api/qwebenginepage_p.h   |  1 +
 .../tst_qquickwebengineview.cpp               | 30 +++++++++++++++++
 .../qwebengineview/tst_qwebengineview.cpp     | 32 +++++++++++++++++++
 8 files changed, 82 insertions(+)

diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index f32e797a5..e6d25a8fb 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -239,6 +239,7 @@ public:
     virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) = 0;
     virtual void requestGeometryChange(const QRect &geometry) = 0;
     virtual void allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController) = 0;
+    virtual bool isEnabled() const = 0;
 
     virtual QSharedPointer<BrowserContextAdapter> browserContextAdapter() = 0;
 
diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp
index 67addacd5..ae53619ac 100644
--- a/src/core/web_contents_view_qt.cpp
+++ b/src/core/web_contents_view_qt.cpp
@@ -113,6 +113,8 @@ void WebContentsViewQt::GetContainerBounds(gfx::Rect* out) const
 
 void WebContentsViewQt::Focus()
 {
+    if (!m_client->isEnabled())
+        return;
     if (content::RenderWidgetHostView *rwhv = m_webContents->GetRenderWidgetHostView())
         rwhv->Focus();
     m_client->focusContainer();
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index a9ca47598..6396e1151 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -1001,6 +1001,12 @@ void QQuickWebEngineViewPrivate::renderProcessTerminated(
                                       renderProcessExitStatus(terminationStatus)), exitCode);
 }
 
+bool QQuickWebEngineViewPrivate::isEnabled() const
+{
+    const Q_Q(QQuickWebEngineView);
+    return q->isEnabled();
+}
+
 bool QQuickWebEngineView::isLoading() const
 {
     Q_D(const QQuickWebEngineView);
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 65360dcac..831f70afd 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -171,6 +171,7 @@ public:
     virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus,
                                      int exitCode) Q_DECL_OVERRIDE;
     virtual void requestGeometryChange(const QRect &geometry) Q_DECL_OVERRIDE { Q_UNUSED(geometry); }
+    virtual bool isEnabled() const Q_DECL_OVERRIDE;
 
     virtual QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContextAdapter() Q_DECL_OVERRIDE;
 
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 7795cc41d..2fd026682 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -1071,6 +1071,15 @@ void QWebEnginePagePrivate::requestGeometryChange(const QRect &geometry)
     Q_EMIT q->geometryChangeRequested(geometry);
 }
 
+bool QWebEnginePagePrivate::isEnabled() const
+{
+    const Q_Q(QWebEnginePage);
+    const QWidget *view = q->view();
+    if (view)
+        return view->isEnabled();
+    return true;
+}
+
 QMenu *QWebEnginePage::createStandardContextMenu()
 {
     Q_D(QWebEnginePage);
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index 3738de3cf..8e5be51d0 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -124,6 +124,7 @@ public:
     virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus,
                                      int exitCode) Q_DECL_OVERRIDE;
     virtual void requestGeometryChange(const QRect &geometry) Q_DECL_OVERRIDE;
+    virtual bool isEnabled() const Q_DECL_OVERRIDE;
 
     virtual QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContextAdapter() Q_DECL_OVERRIDE;
 
diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
index 60e495137..d39a6df54 100644
--- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
+++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
@@ -61,6 +61,8 @@ private Q_SLOTS:
     void inputMethodHints();
     void basicRenderingSanity();
     void setZoomFactor();
+    void stopSettingFocusWhenDisabled();
+    void stopSettingFocusWhenDisabled_data();
 
 private:
     inline QQuickWebEngineView *newWebEngineView();
@@ -481,5 +483,33 @@ void tst_QQuickWebEngineView::setZoomFactor()
     QVERIFY(qFuzzyCompare(view->zoomFactor(), 2.5));
 }
 
+void tst_QQuickWebEngineView::stopSettingFocusWhenDisabled()
+{
+    QFETCH(bool, viewEnabled);
+    QFETCH(bool, activeFocusResult);
+
+    QQuickWebEngineView *view = webEngineView();
+    m_window->show();
+    view->setSize(QSizeF(640, 480));
+    view->setEnabled(viewEnabled);
+    view->loadHtml("<html><head><title>Title</title></head><body>Hello"
+                   "<input id=\"input\" type=\"text\"></body></html>");
+    QVERIFY(waitForLoadSucceeded(view));
+
+    // When enabled, the view should get active focus after the page is loaded.
+    QTRY_COMPARE_WITH_TIMEOUT(view->hasActiveFocus(), activeFocusResult, 1000);
+    view->runJavaScript("document.getElementById(\"input\").focus()");
+    QTRY_COMPARE_WITH_TIMEOUT(view->hasActiveFocus(), activeFocusResult, 1000);
+}
+
+void tst_QQuickWebEngineView::stopSettingFocusWhenDisabled_data()
+{
+    QTest::addColumn<bool>("viewEnabled");
+    QTest::addColumn<bool>("activeFocusResult");
+
+    QTest::newRow("enabled view gets active focus") << true << true;
+    QTest::newRow("disabled view does not get active focus") << false << false;
+}
+
 QTEST_MAIN(tst_QQuickWebEngineView)
 #include "tst_qquickwebengineview.moc"
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index d496362f9..e66bf18cd 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -70,6 +70,8 @@ private Q_SLOTS:
 #endif
     void doNotSendMouseKeyboardEventsWhenDisabled();
     void doNotSendMouseKeyboardEventsWhenDisabled_data();
+    void stopSettingFocusWhenDisabled();
+    void stopSettingFocusWhenDisabled_data();
 };
 
 // This will be called before the first test function is executed.
@@ -709,5 +711,35 @@ void tst_QWebEngineView::doNotSendMouseKeyboardEventsWhenDisabled_data()
     QTest::newRow("disabled view does not receive events") << false << 4;
 }
 
+void tst_QWebEngineView::stopSettingFocusWhenDisabled()
+{
+    QFETCH(bool, viewEnabled);
+    QFETCH(bool, focusResult);
+
+    QWebEngineView webView;
+    webView.resize(640, 480);
+    webView.show();
+    webView.setEnabled(viewEnabled);
+    QTest::qWaitForWindowExposed(&webView);
+
+    QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool)));
+    webView.setHtml("<html><head><title>Title</title></head><body>Hello"
+                    "<input id=\"input\" type=\"text\"></body></html>");
+    QTRY_COMPARE(loadSpy.count(), 1);
+
+    QTRY_COMPARE_WITH_TIMEOUT(webView.hasFocus(), focusResult, 1000);
+    evaluateJavaScriptSync(webView.page(), "document.getElementById(\"input\").focus()");
+    QTRY_COMPARE_WITH_TIMEOUT(webView.hasFocus(), focusResult, 1000);
+}
+
+void tst_QWebEngineView::stopSettingFocusWhenDisabled_data()
+{
+    QTest::addColumn<bool>("viewEnabled");
+    QTest::addColumn<bool>("focusResult");
+
+    QTest::newRow("enabled view gets focus") << true << true;
+    QTest::newRow("disabled view does not get focus") << false << false;
+}
+
 QTEST_MAIN(tst_QWebEngineView)
 #include "tst_qwebengineview.moc"
-- 
GitLab