From 2781345715338669e2573fb0c05efe5656ca37d8 Mon Sep 17 00:00:00 2001
From: Szabolcs David <davidsz@inf.u-szeged.hu>
Date: Fri, 25 Jul 2014 08:40:54 -0700
Subject: [PATCH] Add loadVisuallyCommitted signal to the experimental Quick
 API

This fixes the flaky QQuickWebEngineViewGraphics test and extends it
with a new test case.

Change-Id: I2d8a0762716cb9232fdea6473760e67ac2e7146d
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
---
 src/3rdparty                                  |  2 +-
 src/core/common/qt_messages.h                 |  2 ++
 src/core/qt_render_view_observer_host.cpp     | 11 +++++++
 src/core/qt_render_view_observer_host.h       |  2 ++
 src/core/render_widget_host_view_qt.cpp       | 11 +++++++
 src/core/render_widget_host_view_qt.h         |  3 ++
 src/core/renderer/qt_render_view_observer.cpp |  5 ++++
 src/core/renderer/qt_render_view_observer.h   |  2 ++
 src/core/web_contents_adapter_client.h        |  1 +
 src/webengine/api/qquickwebengineview.cpp     | 11 +++++++
 src/webengine/api/qquickwebengineview_p.h     |  3 ++
 src/webengine/api/qquickwebengineview_p_p.h   |  2 ++
 src/webenginewidgets/api/qwebenginepage_p.h   |  1 +
 .../qquickwebengineviewgraphics.pro           |  1 +
 .../tst_qquickwebengineviewgraphics.cpp       | 29 +++++++++++++++++--
 15 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/src/3rdparty b/src/3rdparty
index 622cb04cc..b45f07bfb 160000
--- a/src/3rdparty
+++ b/src/3rdparty
@@ -1 +1 @@
-Subproject commit 622cb04cc077a10d04c46798af8d132811ac9de3
+Subproject commit b45f07bfbe74c333f1017810c2409e1aa6077a1b
diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h
index 3150a9376..315987cd3 100644
--- a/src/core/common/qt_messages.h
+++ b/src/core/common/qt_messages.h
@@ -40,3 +40,5 @@ IPC_MESSAGE_ROUTED2(QtRenderViewObserverHost_DidFetchDocumentMarkup,
 IPC_MESSAGE_ROUTED2(QtRenderViewObserverHost_DidFetchDocumentInnerText,
                     uint64 /* requestId */,
                     base::string16 /* innerText */)
+
+IPC_MESSAGE_ROUTED0(QtRenderViewObserverHost_DidFirstVisuallyNonEmptyLayout)
diff --git a/src/core/qt_render_view_observer_host.cpp b/src/core/qt_render_view_observer_host.cpp
index be811e0b9..178452a2c 100644
--- a/src/core/qt_render_view_observer_host.cpp
+++ b/src/core/qt_render_view_observer_host.cpp
@@ -42,6 +42,8 @@
 #include "qt_render_view_observer_host.h"
 
 #include "common/qt_messages.h"
+#include "content/public/browser/web_contents.h"
+#include "render_widget_host_view_qt.h"
 #include "type_conversion.h"
 #include "web_contents_adapter_client.h"
 
@@ -69,6 +71,8 @@ bool QtRenderViewObserverHost::OnMessageReceived(const IPC::Message& message)
                             onDidFetchDocumentMarkup)
         IPC_MESSAGE_HANDLER(QtRenderViewObserverHost_DidFetchDocumentInnerText,
                             onDidFetchDocumentInnerText)
+        IPC_MESSAGE_HANDLER(QtRenderViewObserverHost_DidFirstVisuallyNonEmptyLayout,
+                            onDidFirstVisuallyNonEmptyLayout)
         IPC_MESSAGE_UNHANDLED(handled = false)
     IPC_END_MESSAGE_MAP()
     return handled;
@@ -84,3 +88,10 @@ void QtRenderViewObserverHost::onDidFetchDocumentInnerText(quint64 requestId, co
 {
     m_adapterClient->didFetchDocumentInnerText(requestId, toQt(innerText));
 }
+
+void QtRenderViewObserverHost::onDidFirstVisuallyNonEmptyLayout()
+{
+    RenderWidgetHostViewQt *rwhv = static_cast<RenderWidgetHostViewQt*>(web_contents()->GetRenderWidgetHostView());
+    if (rwhv)
+        rwhv->didFirstVisuallyNonEmptyLayout();
+}
diff --git a/src/core/qt_render_view_observer_host.h b/src/core/qt_render_view_observer_host.h
index 375bc816b..374e6b04b 100644
--- a/src/core/qt_render_view_observer_host.h
+++ b/src/core/qt_render_view_observer_host.h
@@ -49,6 +49,7 @@
 namespace content {
     class WebContents;
 }
+
 class WebContentsAdapterClient;
 
 class QtRenderViewObserverHost : public content::WebContentsObserver
@@ -62,6 +63,7 @@ private:
     bool OnMessageReceived(const IPC::Message& message) Q_DECL_OVERRIDE;
     void onDidFetchDocumentMarkup(quint64 requestId, const base::string16& markup);
     void onDidFetchDocumentInnerText(quint64 requestId, const base::string16& innerText);
+    void onDidFirstVisuallyNonEmptyLayout();
 
     WebContentsAdapterClient *m_adapterClient;
 };
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index ef07584a6..77588bc16 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -161,6 +161,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
     , m_gestureRecognizer(ui::GestureRecognizer::Create())
     , m_frameNodeData(new DelegatedFrameNodeData)
     , m_needsDelegatedFrameAck(false)
+    , m_didFirstVisuallyNonEmptyLayout(false)
     , m_adapterClient(0)
     , m_anchorPositionWithinSelection(0)
     , m_cursorPositionWithinSelection(0)
@@ -608,6 +609,11 @@ void RenderWidgetHostViewQt::OnSwapCompositorFrame(uint32 output_surface_id, sco
         m_frameNodeData->frameDevicePixelRatio /= dpiScale;
 
     m_delegate->update();
+
+    if (m_didFirstVisuallyNonEmptyLayout) {
+        m_adapterClient->loadVisuallyCommitted();
+        m_didFirstVisuallyNonEmptyLayout = false;
+    }
 }
 
 void RenderWidgetHostViewQt::GetScreenInfo(blink::WebScreenInfo* results)
@@ -1099,3 +1105,8 @@ QAccessibleInterface *RenderWidgetHostViewQt::GetQtAccessible()
     content::BrowserAccessibilityQt *accQt = static_cast<content::BrowserAccessibilityQt*>(acc);
     return accQt;
 }
+
+void RenderWidgetHostViewQt::didFirstVisuallyNonEmptyLayout()
+{
+    m_didFirstVisuallyNonEmptyLayout = true;
+}
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 7f2090995..b4a75ad89 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -221,6 +221,8 @@ public:
 
     QAccessibleInterface *GetQtAccessible();
 
+    void didFirstVisuallyNonEmptyLayout();
+
 private:
     void sendDelegatedFrameAck();
     void Paint(const gfx::Rect& damage_rect);
@@ -243,6 +245,7 @@ private:
     QExplicitlySharedDataPointer<DelegatedFrameNodeData> m_frameNodeData;
     cc::ReturnedResourceArray m_resourcesToRelease;
     bool m_needsDelegatedFrameAck;
+    bool m_didFirstVisuallyNonEmptyLayout;
     uint32 m_pendingOutputSurfaceId;
 
     WebContentsAdapterClient *m_adapterClient;
diff --git a/src/core/renderer/qt_render_view_observer.cpp b/src/core/renderer/qt_render_view_observer.cpp
index 192ba99f9..3322f092d 100644
--- a/src/core/renderer/qt_render_view_observer.cpp
+++ b/src/core/renderer/qt_render_view_observer.cpp
@@ -70,6 +70,11 @@ void QtRenderViewObserver::onFetchDocumentInnerText(quint64 requestId)
         render_view()->GetWebView()->mainFrame()->document().documentElement().innerText()));
 }
 
+void QtRenderViewObserver::OnFirstVisuallyNonEmptyLayout()
+{
+    Send(new QtRenderViewObserverHost_DidFirstVisuallyNonEmptyLayout(routing_id()));
+}
+
 bool QtRenderViewObserver::OnMessageReceived(const IPC::Message& message)
 {
     bool handled = true;
diff --git a/src/core/renderer/qt_render_view_observer.h b/src/core/renderer/qt_render_view_observer.h
index c37dce16d..491b4052f 100644
--- a/src/core/renderer/qt_render_view_observer.h
+++ b/src/core/renderer/qt_render_view_observer.h
@@ -51,6 +51,8 @@ private:
     void onFetchDocumentMarkup(quint64 requestId);
     void onFetchDocumentInnerText(quint64 requestId);
 
+    void OnFirstVisuallyNonEmptyLayout() Q_DECL_OVERRIDE;
+
     virtual bool OnMessageReceived(const IPC::Message& message) Q_DECL_OVERRIDE;
 
     DISALLOW_COPY_AND_ASSIGN(QtRenderViewObserver);
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index b7836e4ca..f8f729f60 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -138,6 +138,7 @@ public:
     virtual qreal dpiScale() const = 0;
     virtual void loadStarted(const QUrl &provisionalUrl) = 0;
     virtual void loadCommitted() = 0;
+    virtual void loadVisuallyCommitted() = 0;
     virtual void loadFinished(bool success, int error_code = 0, const QString &error_description = QString()) = 0;
     virtual void focusContainer() = 0;
     virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect & initialGeometry) = 0;
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index b71dbfd06..559057b94 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -278,6 +278,11 @@ void QQuickWebEngineViewPrivate::loadCommitted()
     m_history->reset();
 }
 
+void QQuickWebEngineViewPrivate::loadVisuallyCommitted()
+{
+    Q_EMIT e->loadVisuallyCommitted();
+}
+
 void QQuickWebEngineViewPrivate::loadFinished(bool success, int error_code, const QString &error_description)
 {
     Q_Q(QQuickWebEngineView);
@@ -575,6 +580,12 @@ void QQuickWebEngineView::forceActiveFocus()
     }
 }
 
+QQuickWebEngineViewExperimental *QQuickWebEngineView::experimental() const
+{
+    Q_D(const QQuickWebEngineView);
+    return d->e.data();
+}
+
 bool QQuickWebEngineViewExperimental::inspectable() const
 {
     Q_D(const QQuickWebEngineView);
diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h
index fd783eb84..ca2574e0e 100644
--- a/src/webengine/api/qquickwebengineview_p.h
+++ b/src/webengine/api/qquickwebengineview_p.h
@@ -47,6 +47,7 @@
 
 QT_BEGIN_NAMESPACE
 
+class QQuickWebEngineViewExperimental;
 class QQuickWebEngineViewPrivate;
 class QQuickWebEngineLoadRequest;
 
@@ -78,6 +79,8 @@ public:
     bool canGoForward() const;
     void forceActiveFocus();
 
+    QQuickWebEngineViewExperimental *experimental() const;
+
     enum LoadStatus {
         LoadStartedStatus,
         LoadStoppedStatus,
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 299d7b0a5..e4a315d1a 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -117,6 +117,7 @@ Q_SIGNALS:
     void isFullScreenChanged();
     void extraContextMenuEntriesComponentChanged();
     void featurePermissionRequested(const QUrl &securityOrigin, Feature feature);
+    void loadVisuallyCommitted();
 
 private:
     QQuickWebEngineViewExperimental(QQuickWebEngineViewPrivate* viewPrivate);
@@ -151,6 +152,7 @@ public:
     virtual qreal dpiScale() const Q_DECL_OVERRIDE;
     virtual void loadStarted(const QUrl &provisionalUrl) Q_DECL_OVERRIDE;
     virtual void loadCommitted() Q_DECL_OVERRIDE;
+    virtual void loadVisuallyCommitted() Q_DECL_OVERRIDE;
     virtual void loadFinished(bool success, int error_code = 0, const QString &error_description = QString()) Q_DECL_OVERRIDE;
     virtual void focusContainer() Q_DECL_OVERRIDE;
     virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &) Q_DECL_OVERRIDE;
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index a717b6ba8..e46c64af8 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -121,6 +121,7 @@ public:
     virtual qreal dpiScale() const Q_DECL_OVERRIDE;
     virtual void loadStarted(const QUrl &provisionalUrl) Q_DECL_OVERRIDE;
     virtual void loadCommitted() Q_DECL_OVERRIDE;
+    virtual void loadVisuallyCommitted() Q_DECL_OVERRIDE { }
     virtual void loadFinished(bool success, int error_code, const QString &error_description = QString()) Q_DECL_OVERRIDE;
     virtual void focusContainer() Q_DECL_OVERRIDE;
     virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry) Q_DECL_OVERRIDE;
diff --git a/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro b/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro
index ff6c49628..cbd11cdca 100644
--- a/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro
+++ b/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro
@@ -1,2 +1,3 @@
 include(../tests.pri)
 exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
+QT_PRIVATE += webengine-private
diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
index ad72ea0cc..d64ab5d16 100644
--- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
+++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
@@ -45,6 +45,7 @@
 #include <QQuickItem>
 #include <QPainter>
 #include <qtwebengineglobal.h>
+#include <private/qquickwebengineview_p.h>
 
 class TestView : public QQuickView {
     Q_OBJECT
@@ -72,6 +73,7 @@ public Q_SLOTS:
 
 private Q_SLOTS:
     void simpleGraphics();
+    void renderMultipleTimes();
     void renderAfterNodeCleanup();
     void showHideShow();
     void simpleAcceleratedLayer();
@@ -97,6 +99,21 @@ static QImage get150x150GreenReferenceImage()
     return reference;
 }
 
+static inline bool waitForSignal(QObject *obj, const char *signal, int timeout = 10000)
+{
+    QEventLoop loop;
+    QObject::connect(obj, signal, &loop, SLOT(quit()));
+    QTimer timer;
+    QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
+    if (timeout > 0) {
+        QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
+        timer.setSingleShot(true);
+        timer.start(timeout);
+    }
+    loop.exec();
+    return timeoutSpy.isEmpty();
+}
+
 tst_QQuickWebEngineViewGraphics::tst_QQuickWebEngineViewGraphics()
 {
 }
@@ -127,6 +144,14 @@ void tst_QQuickWebEngineViewGraphics::simpleGraphics()
     QCOMPARE(m_view->grabWindow(), get150x150GreenReferenceImage());
 }
 
+void tst_QQuickWebEngineViewGraphics::renderMultipleTimes()
+{
+    // This test is for loadVisuallyCommitted signal.
+    // The setHtml() should not fail after multiple page load.
+    setHtml(greenSquare);
+    setHtml(greenSquare);
+}
+
 void tst_QQuickWebEngineViewGraphics::renderAfterNodeCleanup()
 {
     setHtml(greenSquare);
@@ -177,8 +202,8 @@ void tst_QQuickWebEngineViewGraphics::setHtml(const QString &html)
     m_view->setSource(QUrl(QStringLiteral("data:text/plain,%1").arg(qmlData)));
     m_view->create();
 
-    // FIXME: Replace with a proper initialLayout signal.
-    QTest::qWait(200);
+    QQuickWebEngineView *webEngineView = static_cast<QQuickWebEngineView *>(m_view->rootObject());
+    QVERIFY(waitForSignal(reinterpret_cast<QObject *>(webEngineView->experimental()), SIGNAL(loadVisuallyCommitted())));
     QCOMPARE(m_view->rootObject()->property("loading"), QVariant(false));
 }
 
-- 
GitLab