diff options
Diffstat (limited to 'tests/auto')
76 files changed, 3020 insertions, 2022 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index f14305f72..06430cf8e 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -4,4 +4,6 @@ SUBDIRS = quick qtHaveModule(webenginewidgets) { SUBDIRS += widgets +# core tests depend on widgets for now + SUBDIRS += core } diff --git a/tests/auto/core/core.pro b/tests/auto/core/core.pro new file mode 100644 index 000000000..ed0a61532 --- /dev/null +++ b/tests/auto/core/core.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +CONFIG += ordered + +SUBDIRS += \ + qwebenginecookiestoreclient \ + qwebengineurlrequestinterceptor \ diff --git a/tests/auto/core/qwebenginecookiestoreclient/qwebenginecookiestoreclient.pro b/tests/auto/core/qwebenginecookiestoreclient/qwebenginecookiestoreclient.pro new file mode 100644 index 000000000..e99c7f493 --- /dev/null +++ b/tests/auto/core/qwebenginecookiestoreclient/qwebenginecookiestoreclient.pro @@ -0,0 +1 @@ +include(../tests.pri) diff --git a/tests/auto/core/qwebenginecookiestoreclient/resources/content.html b/tests/auto/core/qwebenginecookiestoreclient/resources/content.html new file mode 100644 index 000000000..360ad65ef --- /dev/null +++ b/tests/auto/core/qwebenginecookiestoreclient/resources/content.html @@ -0,0 +1,5 @@ +<html> +<body> +<a>This is test content</a> +</body> +</html> diff --git a/tests/auto/core/qwebenginecookiestoreclient/resources/index.html b/tests/auto/core/qwebenginecookiestoreclient/resources/index.html new file mode 100644 index 000000000..d41866712 --- /dev/null +++ b/tests/auto/core/qwebenginecookiestoreclient/resources/index.html @@ -0,0 +1,38 @@ +<html> + <head> + <script type="text/javascript"> + function generateCookieString(key, value, options) { + key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent); + key = key.replace(/\(/g, '%28').replace(/\)/g, '%29'); + value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent); + options = options || {}; + + var cookieString = key + '=' + value; + cookieString += options.path ? '; Path=' + options.path : ''; + cookieString += options.domain ? '; Domain=' + options.domain : ''; + cookieString += options.expires ? '; Expires=' + options.expires.toUTCString() : ''; + cookieString += options.secure ? '; Secure' : ''; + + console.log(cookieString) + return cookieString; +}; +function setCookie() { + var name = "SessionCookie" + var value = "QtWebEngineCookieTest" + document.cookie = generateCookieString(name, value, {}) + + name = "CookieWithExpiresField" + value = "QtWebEngineCookieTest" + var daysValid = 10; + var date = new Date(); + date.setTime(date.getTime() + (daysValid*24*60*60*1000)); + var expires = date; + var options = {}; + options.expires = expires; + document.cookie = generateCookieString(name, value, options) +} +</script> + </head> + <body onload="setCookie()"> + </body> +</html> diff --git a/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.cpp b/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.cpp new file mode 100644 index 000000000..d78a81c21 --- /dev/null +++ b/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../widgets/util.h" +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebenginecallback.h> +#include <QtWebEngineCore/qwebenginecookiestoreclient.h> +#include <QtWebEngineWidgets/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebengineview.h> + +class tst_QWebEngineCookieStoreClient : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineCookieStoreClient(); + ~tst_QWebEngineCookieStoreClient(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void cookieSignals(); + void setAndDeleteCookie(); + void batchCookieTasks(); +}; + +tst_QWebEngineCookieStoreClient::tst_QWebEngineCookieStoreClient() +{ +} + +tst_QWebEngineCookieStoreClient::~tst_QWebEngineCookieStoreClient() +{ +} + +void tst_QWebEngineCookieStoreClient::init() +{ +} + +void tst_QWebEngineCookieStoreClient::cleanup() +{ +} + +void tst_QWebEngineCookieStoreClient::initTestCase() +{ +} + +void tst_QWebEngineCookieStoreClient::cleanupTestCase() +{ +} + +void tst_QWebEngineCookieStoreClient::cookieSignals() +{ + QWebEngineView view; + QWebEngineCookieStoreClient *client = view.page()->profile()->cookieStoreClient(); + + QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &))); + QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &))); + + view.load(QUrl("qrc:///resources/index.html")); + + QTRY_COMPARE(loadSpy.count(), 1); + QVariant success = loadSpy.takeFirst().takeFirst(); + QVERIFY(success.toBool()); + QTRY_COMPARE(cookieAddedSpy.count(), 2); + + // try whether updating a cookie to be expired results in that cookie being removed. + QNetworkCookie expiredCookie(QNetworkCookie::parseCookies(QByteArrayLiteral("SessionCookie=delete; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=///resources")).first()); + client->setCookie(expiredCookie, QUrl("qrc:///resources/index.html")); + QTRY_COMPARE(cookieRemovedSpy.count(), 1); + cookieRemovedSpy.clear(); + + // try removing the other cookie. + QNetworkCookie nonSessionCookie(QNetworkCookie::parseCookies(QByteArrayLiteral("CookieWithExpiresField=QtWebEngineCookieTest; path=///resources")).first()); + client->deleteCookie(nonSessionCookie, QUrl("qrc:///resources/index.html")); + QTRY_COMPARE(cookieRemovedSpy.count(), 1); +} + +void tst_QWebEngineCookieStoreClient::setAndDeleteCookie() +{ + QWebEngineView view; + QWebEngineCookieStoreClient *client = view.page()->profile()->cookieStoreClient(); + + QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &))); + QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &))); + + QNetworkCookie cookie1(QNetworkCookie::parseCookies(QByteArrayLiteral("khaos=I9GX8CWI; Domain=.example.com; Path=/docs")).first()); + QNetworkCookie cookie2(QNetworkCookie::parseCookies(QByteArrayLiteral("Test%20Cookie=foobar; domain=example.com; Path=/")).first()); + QNetworkCookie cookie3(QNetworkCookie::parseCookies(QByteArrayLiteral("SessionCookie=QtWebEngineCookieTest; Path=///resources")).first()); + QNetworkCookie expiredCookie3(QNetworkCookie::parseCookies(QByteArrayLiteral("SessionCookie=delete; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=///resources")).first()); + + // check if pending cookies are set and removed + client->setCookieWithCallback(cookie1, [](bool success) { QVERIFY(success); }); + client->setCookieWithCallback(cookie2, [](bool success) { QVERIFY(success); }); + client->deleteCookie(cookie1); + + view.load(QUrl("qrc:///resources/content.html")); + + QTRY_COMPARE(loadSpy.count(), 1); + QVariant success = loadSpy.takeFirst().takeFirst(); + QVERIFY(success.toBool()); + QTRY_COMPARE(cookieAddedSpy.count(), 2); + QTRY_COMPARE(cookieRemovedSpy.count(), 1); + cookieAddedSpy.clear(); + cookieRemovedSpy.clear(); + + client->setCookieWithCallback(cookie3, [](bool success) { QVERIFY(success); }); + // updating a cookie with an expired 'expires' field should remove the cookie with the same name + client->setCookieWithCallback(expiredCookie3, [](bool success) { QVERIFY(success); }); + client->deleteCookie(cookie2); + QTRY_COMPARE(cookieAddedSpy.count(), 1); + QTRY_COMPARE(cookieRemovedSpy.count(), 2); +} + +void tst_QWebEngineCookieStoreClient::batchCookieTasks() +{ + QWebEngineView view; + QWebEngineCookieStoreClient *client = view.page()->profile()->cookieStoreClient(); + + QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &))); + QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &))); + + QNetworkCookie cookie1(QNetworkCookie::parseCookies(QByteArrayLiteral("khaos=I9GX8CWI; Domain=.example.com; Path=/docs")).first()); + QNetworkCookie cookie2(QNetworkCookie::parseCookies(QByteArrayLiteral("Test%20Cookie=foobar; domain=example.com; Path=/")).first()); + + int capture = 0; + + client->setCookieWithCallback(cookie1, [&capture](bool success) { QVERIFY(success); ++capture; }); + client->setCookieWithCallback(cookie2, [&capture](bool success) { QVERIFY(success); ++capture; }); + + view.load(QUrl("qrc:///resources/index.html")); + + QTRY_COMPARE(loadSpy.count(), 1); + QVariant success = loadSpy.takeFirst().takeFirst(); + QVERIFY(success.toBool()); + QTRY_COMPARE(cookieAddedSpy.count(), 4); + QTRY_COMPARE(cookieRemovedSpy.count(), 0); + QTRY_COMPARE(capture, 2); + capture = 0; + + cookieAddedSpy.clear(); + cookieRemovedSpy.clear(); + + client->getAllCookies([&capture](const QByteArray& cookieLine) { + ++capture; + QCOMPARE(QNetworkCookie::parseCookies(cookieLine).count(), 4); + }); + + client->deleteSessionCookiesWithCallback([&capture](int numDeleted) { + ++capture; + QCOMPARE(numDeleted, 3); + }); + + client->deleteAllCookiesWithCallback([&capture](int numDeleted) { + ++capture; + QCOMPARE(numDeleted, 1); + }); + + QTRY_COMPARE(capture, 3); +} + +QTEST_MAIN(tst_QWebEngineCookieStoreClient) +#include "tst_qwebenginecookiestoreclient.moc" diff --git a/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.qrc b/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.qrc new file mode 100644 index 000000000..afeae268b --- /dev/null +++ b/tests/auto/core/qwebenginecookiestoreclient/tst_qwebenginecookiestoreclient.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/index.html</file> + <file>resources/content.html</file> +</qresource> +</RCC> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro b/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro new file mode 100644 index 000000000..e99c7f493 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro @@ -0,0 +1 @@ +include(../tests.pri) diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/content.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/content.html new file mode 100644 index 000000000..360ad65ef --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/content.html @@ -0,0 +1,5 @@ +<html> +<body> +<a>This is test content</a> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/firstparty.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/firstparty.html new file mode 100644 index 000000000..8bc540c08 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/firstparty.html @@ -0,0 +1,5 @@ +<html> +<body> +<iframe src="qrc:///resources/content.html"></iframe> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/index.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/index.html new file mode 100644 index 000000000..a744dbd99 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/index.html @@ -0,0 +1,16 @@ +<html> + <head> + <script type="text/javascript"> + function post() { + var form = document.createElement("form"); + form.setAttribute("method", "POST"); + document.body.appendChild(form); + form.submit(); + return true; + } +</script> + </head> + <body> + <h1>Test page</h1> + </body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp new file mode 100644 index 000000000..4891cafd0 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../widgets/util.h" +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> +#include <QtWebEngineWidgets/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebenginesettings.h> +#include <QtWebEngineWidgets/qwebengineview.h> + +class tst_QWebEngineUrlRequestInterceptor : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineUrlRequestInterceptor(); + ~tst_QWebEngineUrlRequestInterceptor(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void interceptRequest(); + void ipv6HostEncoding(); + void requestedUrl(); + void setUrlSameUrl(); + void firstPartyUrl(); +}; + +tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() +{ +} + +tst_QWebEngineUrlRequestInterceptor::~tst_QWebEngineUrlRequestInterceptor() +{ +} + +void tst_QWebEngineUrlRequestInterceptor::init() +{ +} + +void tst_QWebEngineUrlRequestInterceptor::cleanup() +{ +} + +void tst_QWebEngineUrlRequestInterceptor::initTestCase() +{ +} + +void tst_QWebEngineUrlRequestInterceptor::cleanupTestCase() +{ +} + +class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + QList<QUrl> observedUrls; + QList<QUrl> firstPartyUrls; + bool shouldIntercept; + + bool interceptRequest(QWebEngineUrlRequestInfo &info) override + { + info.block(info.requestMethod() != QByteArrayLiteral("GET")); + if (info.requestUrl().toString().endsWith(QLatin1String("__placeholder__"))) + info.redirect(QUrl("qrc:///resources/content.html")); + + observedUrls.append(info.requestUrl()); + firstPartyUrls.append(info.firstPartyUrl()); + return shouldIntercept; + } + TestRequestInterceptor(bool intercept) + : shouldIntercept(intercept) + { + } +}; + +void tst_QWebEngineUrlRequestInterceptor::interceptRequest() +{ + QWebEngineView view; + TestRequestInterceptor interceptor(/* intercept */ true); + + QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + view.page()->profile()->setRequestInterceptor(&interceptor); + view.load(QUrl("qrc:///resources/index.html")); + QTRY_COMPARE(loadSpy.count(), 1); + QVariant success = loadSpy.takeFirst().takeFirst(); + QVERIFY(success.toBool()); + loadSpy.clear(); + QVariant ok; + + view.page()->runJavaScript("post();", [&ok](const QVariant result){ ok = result; }); + QTRY_VERIFY(ok.toBool()); + QTRY_COMPARE(loadSpy.count(), 1); + success = loadSpy.takeFirst().takeFirst(); + // We block non-GET requests, so this should not succeed. + QVERIFY(!success.toBool()); + loadSpy.clear(); + + view.load(QUrl("qrc:///resources/__placeholder__")); + QTRY_COMPARE(loadSpy.count(), 1); + success = loadSpy.takeFirst().takeFirst(); + // The redirection for __placeholder__ should succeed. + QVERIFY(success.toBool()); + loadSpy.clear(); + QCOMPARE(interceptor.observedUrls.count(), 4); + + + // Make sure that registering an observer does not modify the request. + TestRequestInterceptor observer(/* intercept */ false); + view.page()->profile()->setRequestInterceptor(&observer); + view.load(QUrl("qrc:///resources/__placeholder__")); + QTRY_COMPARE(loadSpy.count(), 1); + success = loadSpy.takeFirst().takeFirst(); + // Since we do not intercept, loading an invalid path should not succeed. + QVERIFY(!success.toBool()); + QCOMPARE(observer.observedUrls.count(), 1); +} + +class LocalhostContentProvider : public QWebEngineUrlRequestInterceptor +{ +public: + LocalhostContentProvider() { } + + bool interceptRequest(QWebEngineUrlRequestInfo &info) override + { + requestedUrls.append(info.requestUrl()); + info.redirect(QUrl("data:text/html,<p>hello")); + return true; + } + + QList<QUrl> requestedUrls; +}; + +void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() +{ + QWebEngineView view; + QWebEnginePage *page = view.page(); + LocalhostContentProvider contentProvider; + QSignalSpy spyLoadFinished(page, SIGNAL(loadFinished(bool))); + + page->profile()->setRequestInterceptor(&contentProvider); + + page->setHtml("<p>Hi", QUrl::fromEncoded("http://[::1]/index.html")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + QCOMPARE(contentProvider.requestedUrls.count(), 0); + + evaluateJavaScriptSync(page, "var r = new XMLHttpRequest();" + "r.open('GET', 'http://[::1]/test.xml', false);" + "r.send(null);" + ); + + QCOMPARE(contentProvider.requestedUrls.count(), 1); + QCOMPARE(contentProvider.requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); +} + +void tst_QWebEngineUrlRequestInterceptor::requestedUrl() +{ + QWebEnginePage page; + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + TestRequestInterceptor interceptor(/* intercept */ true); + page.profile()->setRequestInterceptor(&interceptor); + + page.setUrl(QUrl("qrc:///resources/__placeholder__")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(spy.count(), 1); + QCOMPARE(interceptor.observedUrls.at(0), QUrl("qrc:///resources/content.html")); + QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + + page.setUrl(QUrl("qrc:/non-existent.html")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(spy.count(), 2); + QCOMPARE(interceptor.observedUrls.at(2), QUrl("qrc:/non-existent.html")); + QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + + page.setUrl(QUrl("http://abcdef.abcdef")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(spy.count(), 3); + QCOMPARE(interceptor.observedUrls.at(3), QUrl("http://abcdef.abcdef/")); + QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); +} + +void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() +{ + QWebEnginePage page; + TestRequestInterceptor interceptor(/* intercept */ true); + page.profile()->setRequestInterceptor(&interceptor); + + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.setUrl(QUrl("qrc:///resources/__placeholder__")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + QCOMPARE(spy.count(), 1); + + page.setUrl(QUrl("qrc:///resources/__placeholder__")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + QCOMPARE(spy.count(), 2); + + // Now a case without redirect. + page.setUrl(QUrl("qrc:///resources/content.html")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + QCOMPARE(spy.count(), 3); + + page.setUrl(QUrl("qrc:///resources/__placeholder__")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + QCOMPARE(spy.count(), 4); +} + +void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() +{ + QWebEnginePage page; + TestRequestInterceptor interceptor(/* intercept */ false); + page.profile()->setRequestInterceptor(&interceptor); + + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.setUrl(QUrl("qrc:///resources/firstparty.html")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(interceptor.observedUrls.at(0), QUrl("qrc:///resources/firstparty.html")); + QCOMPARE(interceptor.observedUrls.at(1), QUrl("qrc:///resources/content.html")); + QCOMPARE(interceptor.firstPartyUrls.at(0), QUrl("qrc:///resources/firstparty.html")); + QCOMPARE(interceptor.firstPartyUrls.at(1), QUrl("qrc:///resources/firstparty.html")); + QCOMPARE(spy.count(), 1); +} + +QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) +#include "tst_qwebengineurlrequestinterceptor.moc" diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc new file mode 100644 index 000000000..ca045e7fc --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/index.html</file> + <file>resources/content.html</file> + <file>resources/firstparty.html</file> +</qresource> +</RCC> diff --git a/tests/auto/core/tests.pri b/tests/auto/core/tests.pri new file mode 100644 index 000000000..cd6ef8615 --- /dev/null +++ b/tests/auto/core/tests.pri @@ -0,0 +1,17 @@ +TEMPLATE = app + +# FIXME: Re-enable once we want to run tests on the CI +# CONFIG += testcase + +CONFIG += c++11 + +VPATH += $$_PRO_FILE_PWD_ +TARGET = tst_$$TARGET + +SOURCES += $${TARGET}.cpp +INCLUDEPATH += $$PWD + +exists($$_PRO_FILE_PWD_/$${TARGET}.qrc): RESOURCES += $${TARGET}.qrc + +QT += testlib network webenginewidgets widgets +osx: CONFIG -= app_bundle diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 28a77d8cd..b5894a248 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -86,7 +86,7 @@ void tst_InspectorServer::prepareWebViewComponent() m_component.reset(new QQmlComponent(engine, this)); m_component->setData(QByteArrayLiteral("import QtQuick 2.0\n" - "import QtWebEngine 1.1\n" + "import QtWebEngine 1.2\n" "WebEngineView { }") , QUrl()); } diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 8f0c2f6ec..bf0192e42 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -56,6 +56,7 @@ #include <private/qquickwebengineprofile_p.h> #include <private/qquickwebenginescript_p.h> #include <private/qquickwebenginesettings_p.h> +#include <private/qquickwebenginesingleton_p.h> class tst_publicapi : public QObject { Q_OBJECT @@ -76,6 +77,7 @@ static QList<const QMetaObject *> typesToCheck = QList<const QMetaObject *>() << &QQuickWebEngineScript::staticMetaObject << &QQuickWebEngineSettings::staticMetaObject << &QQuickWebEngineFullScreenRequest::staticMetaObject + << &QQuickWebEngineSingleton::staticMetaObject ; static QList<const char *> knownEnumNames = QList<const char *>(); @@ -87,28 +89,22 @@ static QStringList hardcodedTypes = QStringList() // Ignore the testSupport types without making a fuss. << "QQuickWebEngineTestSupport*" << "QQuickWebEngineErrorPage*" + << "QWebEngineCookieStoreClient*" ; static QStringList expectedAPI = QStringList() << "QQuickWebEngineView.AcceptRequest --> NavigationRequestAction" << "QQuickWebEngineView.IgnoreRequest --> NavigationRequestAction" + << "QQuickWebEngineView.LinkClickedNavigation --> NavigationType" + << "QQuickWebEngineView.TypedNavigation --> NavigationType" + << "QQuickWebEngineView.FormSubmittedNavigation --> NavigationType" + << "QQuickWebEngineView.BackForwardNavigation --> NavigationType" + << "QQuickWebEngineView.ReloadNavigation --> NavigationType" + << "QQuickWebEngineView.OtherNavigation --> NavigationType" << "QQuickWebEngineView.LoadStartedStatus --> LoadStatus" << "QQuickWebEngineView.LoadStoppedStatus --> LoadStatus" << "QQuickWebEngineView.LoadSucceededStatus --> LoadStatus" << "QQuickWebEngineView.LoadFailedStatus --> LoadStatus" - << "QQuickWebEngineCertificateError.SslPinnedKeyNotInCertificateChain --> Error" - << "QQuickWebEngineCertificateError.CertificateCommonNameInvalid --> Error" - << "QQuickWebEngineCertificateError.CertificateDateInvalid --> Error" - << "QQuickWebEngineCertificateError.CertificateAuthorityInvalid --> Error" - << "QQuickWebEngineCertificateError.CertificateContainsErrors --> Error" - << "QQuickWebEngineCertificateError.CertificateNoRevocationMechanism --> Error" - << "QQuickWebEngineCertificateError.CertificateUnableToCheckRevocation --> Error" - << "QQuickWebEngineCertificateError.CertificateRevoked --> Error" - << "QQuickWebEngineCertificateError.CertificateInvalid --> Error" - << "QQuickWebEngineCertificateError.CertificateWeakSignatureAlgorithm --> Error" - << "QQuickWebEngineCertificateError.CertificateNonUniqueName --> Error" - << "QQuickWebEngineCertificateError.CertificateWeakKey --> Error" - << "QQuickWebEngineCertificateError.CertificateNameConstraintViolation --> Error" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" << "QQuickWebEngineView.InternalErrorDomain --> ErrorDomain" << "QQuickWebEngineView.ConnectionErrorDomain --> ErrorDomain" @@ -116,14 +112,6 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineView.HttpErrorDomain --> ErrorDomain" << "QQuickWebEngineView.FtpErrorDomain --> ErrorDomain" << "QQuickWebEngineView.DnsErrorDomain --> ErrorDomain" - << "QQuickWebEngineView.FindBackward --> FindFlags" - << "QQuickWebEngineView.FindCaseSensitively --> FindFlags" - << "QQuickWebEngineView.LinkClickedNavigation --> NavigationType" - << "QQuickWebEngineView.TypedNavigation --> NavigationType" - << "QQuickWebEngineView.FormSubmittedNavigation --> NavigationType" - << "QQuickWebEngineView.BackForwardNavigation --> NavigationType" - << "QQuickWebEngineView.ReloadNavigation --> NavigationType" - << "QQuickWebEngineView.OtherNavigation --> NavigationType" << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination" << "QQuickWebEngineView.NewViewInTab --> NewViewDestination" << "QQuickWebEngineView.NewViewInDialog --> NewViewDestination" @@ -132,31 +120,81 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineView.MediaVideoCapture --> Feature" << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature" << "QQuickWebEngineView.Geolocation --> Feature" + << "QQuickWebEngineView.NoWebAction --> WebAction" + << "QQuickWebEngineView.Back --> WebAction" + << "QQuickWebEngineView.Forward --> WebAction" + << "QQuickWebEngineView.Stop --> WebAction" + << "QQuickWebEngineView.Reload --> WebAction" + << "QQuickWebEngineView.Cut --> WebAction" + << "QQuickWebEngineView.Copy --> WebAction" + << "QQuickWebEngineView.Paste --> WebAction" + << "QQuickWebEngineView.Undo --> WebAction" + << "QQuickWebEngineView.Redo --> WebAction" + << "QQuickWebEngineView.SelectAll --> WebAction" + << "QQuickWebEngineView.ReloadAndBypassCache --> WebAction" + << "QQuickWebEngineView.PasteAndMatchStyle --> WebAction" + << "QQuickWebEngineView.OpenLinkInThisWindow --> WebAction" + << "QQuickWebEngineView.OpenLinkInNewWindow --> WebAction" + << "QQuickWebEngineView.OpenLinkInNewTab --> WebAction" + << "QQuickWebEngineView.CopyLinkToClipboard --> WebAction" + << "QQuickWebEngineView.DownloadLinkToDisk --> WebAction" + << "QQuickWebEngineView.CopyImageToClipboard --> WebAction" + << "QQuickWebEngineView.CopyImageUrlToClipboard --> WebAction" + << "QQuickWebEngineView.DownloadImageToDisk --> WebAction" + << "QQuickWebEngineView.CopyMediaUrlToClipboard --> WebAction" + << "QQuickWebEngineView.ToggleMediaControls --> WebAction" + << "QQuickWebEngineView.ToggleMediaLoop --> WebAction" + << "QQuickWebEngineView.ToggleMediaPlayPause --> WebAction" + << "QQuickWebEngineView.ToggleMediaMute --> WebAction" + << "QQuickWebEngineView.DownloadMediaToDisk --> WebAction" + << "QQuickWebEngineView.InspectElement --> WebAction" + << "QQuickWebEngineView.ExitFullScreen --> WebAction" + << "QQuickWebEngineView.WebActionCount --> WebAction" << "QQuickWebEngineView.InfoMessageLevel --> JavaScriptConsoleMessageLevel" << "QQuickWebEngineView.WarningMessageLevel --> JavaScriptConsoleMessageLevel" << "QQuickWebEngineView.ErrorMessageLevel --> JavaScriptConsoleMessageLevel" - << "QQuickWebEngineView.title --> QString" + << "QQuickWebEngineView.NormalTerminationStatus --> RenderProcessTerminationStatus" + << "QQuickWebEngineView.AbnormalTerminationStatus --> RenderProcessTerminationStatus" + << "QQuickWebEngineView.CrashedTerminationStatus --> RenderProcessTerminationStatus" + << "QQuickWebEngineView.KilledTerminationStatus --> RenderProcessTerminationStatus" + << "QQuickWebEngineView.FindBackward --> FindFlags" + << "QQuickWebEngineView.FindCaseSensitively --> FindFlags" << "QQuickWebEngineView.url --> QUrl" << "QQuickWebEngineView.icon --> QUrl" + << "QQuickWebEngineView.loading --> bool" + << "QQuickWebEngineView.loadProgress --> int" + << "QQuickWebEngineView.title --> QString" << "QQuickWebEngineView.canGoBack --> bool" << "QQuickWebEngineView.canGoForward --> bool" << "QQuickWebEngineView.isFullScreen --> bool" - << "QQuickWebEngineView.loading --> bool" - << "QQuickWebEngineView.loadProgress --> int" + << "QQuickWebEngineView.zoomFactor --> double" + << "QQuickWebEngineView.profile --> QQuickWebEngineProfile*" + << "QQuickWebEngineView.settings --> QQuickWebEngineSettings*" + << "QQuickWebEngineView.navigationHistory --> QQuickWebEngineHistory*" + << "QQuickWebEngineView.webChannel --> QQmlWebChannel*" + << "QQuickWebEngineView.userScripts --> QQmlListProperty<QQuickWebEngineScript>" + << "QQuickWebEngineView.activeFocusOnPress --> bool" + << "QQuickWebEngineView.backgroundColor --> QColor" + << "QQuickWebEngineView.testSupport --> QQuickWebEngineTestSupport*" << "QQuickWebEngineView.titleChanged() --> void" - << "QQuickWebEngineView.loadingChanged(QQuickWebEngineLoadRequest*) --> void" - << "QQuickWebEngineView.certificateError(QQuickWebEngineCertificateError*) --> void" - << "QQuickWebEngineView.loadProgressChanged() --> void" - << "QQuickWebEngineView.javaScriptConsoleMessage(JavaScriptConsoleMessageLevel,QString,int,QString) --> void" << "QQuickWebEngineView.urlChanged() --> void" << "QQuickWebEngineView.iconChanged() --> void" + << "QQuickWebEngineView.loadingChanged(QQuickWebEngineLoadRequest*) --> void" + << "QQuickWebEngineView.loadProgressChanged() --> void" << "QQuickWebEngineView.linkHovered(QUrl) --> void" << "QQuickWebEngineView.navigationRequested(QQuickWebEngineNavigationRequest*) --> void" + << "QQuickWebEngineView.javaScriptConsoleMessage(JavaScriptConsoleMessageLevel,QString,int,QString) --> void" + << "QQuickWebEngineView.certificateError(QQuickWebEngineCertificateError*) --> void" << "QQuickWebEngineView.fullScreenRequested(QQuickWebEngineFullScreenRequest) --> void" << "QQuickWebEngineView.isFullScreenChanged() --> void" - << "QQuickWebEngineView.fullScreenCancelled() --> void" << "QQuickWebEngineView.featurePermissionRequested(QUrl,Feature) --> void" - << "QQuickWebEngineView.grantFeaturePermission(QUrl,Feature,bool) --> void" + << "QQuickWebEngineView.newViewRequested(QQuickWebEngineNewViewRequest*) --> void" + << "QQuickWebEngineView.zoomFactorChanged(double) --> void" + << "QQuickWebEngineView.profileChanged() --> void" + << "QQuickWebEngineView.webChannelChanged() --> void" + << "QQuickWebEngineView.activeFocusOnPressChanged(bool) --> void" + << "QQuickWebEngineView.backgroundColorChanged() --> void" + << "QQuickWebEngineView.renderProcessTerminated(RenderProcessTerminationStatus,int) --> void" << "QQuickWebEngineView.runJavaScript(QString,QJSValue) --> void" << "QQuickWebEngineView.runJavaScript(QString) --> void" << "QQuickWebEngineView.loadHtml(QString,QUrl) --> void" @@ -164,37 +202,50 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineView.goBack() --> void" << "QQuickWebEngineView.goForward() --> void" << "QQuickWebEngineView.goBackOrForward(int) --> void" - << "QQuickWebEngineView.stop() --> void" << "QQuickWebEngineView.reload() --> void" - << "QQuickWebEngineView.zoomFactor --> double" - << "QQuickWebEngineView.zoomFactorChanged(double) --> void" - << "QQuickWebEngineView.profile --> QQuickWebEngineProfile*" - << "QQuickWebEngineView.profileChanged() --> void" - << "QQuickWebEngineView.navigationHistory --> QQuickWebEngineHistory*" - << "QQuickWebEngineView.newViewRequested(QQuickWebEngineNewViewRequest*) --> void" - << "QQuickWebEngineView.userScripts --> QQmlListProperty<QQuickWebEngineScript>" - << "QQuickWebEngineView.settings --> QQuickWebEngineSettings*" - << "QQuickWebEngineView.testSupport --> QQuickWebEngineTestSupport*" - << "QQuickWebEngineView.webChannel --> QQmlWebChannel*" - << "QQuickWebEngineView.webChannelChanged() --> void" << "QQuickWebEngineView.reloadAndBypassCache() --> void" + << "QQuickWebEngineView.stop() --> void" << "QQuickWebEngineView.findText(QString,FindFlags,QJSValue) --> void" << "QQuickWebEngineView.findText(QString,FindFlags) --> void" << "QQuickWebEngineView.findText(QString) --> void" - << "QQuickWebEngineDownloadItem.id --> uint" - << "QQuickWebEngineDownloadItem.state --> DownloadState" - << "QQuickWebEngineDownloadItem.path --> QString" - << "QQuickWebEngineDownloadItem.totalBytes --> qlonglong" - << "QQuickWebEngineDownloadItem.receivedBytes --> qlonglong" + << "QQuickWebEngineView.fullScreenCancelled() --> void" + << "QQuickWebEngineView.grantFeaturePermission(QUrl,Feature,bool) --> void" + << "QQuickWebEngineView.setActiveFocusOnPress(bool) --> void" + << "QQuickWebEngineView.triggerWebAction(WebAction) --> void" + << "QQuickWebEngineCertificateError.SslPinnedKeyNotInCertificateChain --> Error" + << "QQuickWebEngineCertificateError.CertificateCommonNameInvalid --> Error" + << "QQuickWebEngineCertificateError.CertificateDateInvalid --> Error" + << "QQuickWebEngineCertificateError.CertificateAuthorityInvalid --> Error" + << "QQuickWebEngineCertificateError.CertificateContainsErrors --> Error" + << "QQuickWebEngineCertificateError.CertificateNoRevocationMechanism --> Error" + << "QQuickWebEngineCertificateError.CertificateUnableToCheckRevocation --> Error" + << "QQuickWebEngineCertificateError.CertificateRevoked --> Error" + << "QQuickWebEngineCertificateError.CertificateInvalid --> Error" + << "QQuickWebEngineCertificateError.CertificateWeakSignatureAlgorithm --> Error" + << "QQuickWebEngineCertificateError.CertificateNonUniqueName --> Error" + << "QQuickWebEngineCertificateError.CertificateWeakKey --> Error" + << "QQuickWebEngineCertificateError.CertificateNameConstraintViolation --> Error" + << "QQuickWebEngineCertificateError.url --> QUrl" + << "QQuickWebEngineCertificateError.error --> Error" + << "QQuickWebEngineCertificateError.description --> QString" + << "QQuickWebEngineCertificateError.overridable --> bool" + << "QQuickWebEngineCertificateError.defer() --> void" + << "QQuickWebEngineCertificateError.ignoreCertificateError() --> void" + << "QQuickWebEngineCertificateError.rejectCertificate() --> void" << "QQuickWebEngineDownloadItem.DownloadRequested --> DownloadState" << "QQuickWebEngineDownloadItem.DownloadInProgress --> DownloadState" << "QQuickWebEngineDownloadItem.DownloadCompleted --> DownloadState" << "QQuickWebEngineDownloadItem.DownloadCancelled --> DownloadState" << "QQuickWebEngineDownloadItem.DownloadInterrupted --> DownloadState" + << "QQuickWebEngineDownloadItem.id --> uint" + << "QQuickWebEngineDownloadItem.state --> DownloadState" + << "QQuickWebEngineDownloadItem.totalBytes --> qlonglong" + << "QQuickWebEngineDownloadItem.receivedBytes --> qlonglong" + << "QQuickWebEngineDownloadItem.path --> QString" << "QQuickWebEngineDownloadItem.stateChanged() --> void" - << "QQuickWebEngineDownloadItem.pathChanged() --> void" << "QQuickWebEngineDownloadItem.receivedBytesChanged() --> void" << "QQuickWebEngineDownloadItem.totalBytesChanged() --> void" + << "QQuickWebEngineDownloadItem.pathChanged() --> void" << "QQuickWebEngineDownloadItem.accept() --> void" << "QQuickWebEngineDownloadItem.cancel() --> void" << "QQuickWebEngineHistory.items --> QQuickWebEngineHistoryListModel*" @@ -224,6 +275,7 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.cachePath --> QString" << "QQuickWebEngineProfile.httpUserAgent --> QString" << "QQuickWebEngineProfile.httpCacheType --> HttpCacheType" + << "QQuickWebEngineProfile.httpAcceptLanguage --> QString" << "QQuickWebEngineProfile.persistentCookiesPolicy --> PersistentCookiesPolicy" << "QQuickWebEngineProfile.httpCacheMaximumSize --> int" << "QQuickWebEngineProfile.storageNameChanged() --> void" @@ -234,39 +286,10 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.httpCacheTypeChanged() --> void" << "QQuickWebEngineProfile.persistentCookiesPolicyChanged() --> void" << "QQuickWebEngineProfile.httpCacheMaximumSizeChanged() --> void" + << "QQuickWebEngineProfile.httpAcceptLanguageChanged() --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadItem*) --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadItem*) --> void" - << "QQuickWebEngineSettings.autoLoadImages --> bool" - << "QQuickWebEngineSettings.javascriptEnabled --> bool" - << "QQuickWebEngineSettings.javascriptCanOpenWindows --> bool" - << "QQuickWebEngineSettings.javascriptCanAccessClipboard --> bool" - << "QQuickWebEngineSettings.linksIncludedInFocusChain --> bool" - << "QQuickWebEngineSettings.localStorageEnabled --> bool" - << "QQuickWebEngineSettings.localContentCanAccessRemoteUrls --> bool" - << "QQuickWebEngineSettings.spatialNavigationEnabled --> bool" - << "QQuickWebEngineSettings.localContentCanAccessFileUrls --> bool" - << "QQuickWebEngineSettings.hyperlinkAuditingEnabled --> bool" - << "QQuickWebEngineSettings.errorPageEnabled --> bool" - << "QQuickWebEngineSettings.defaultTextEncoding --> QString" - << "QQuickWebEngineSettings.autoLoadImagesChanged() --> void" - << "QQuickWebEngineSettings.javascriptEnabledChanged() --> void" - << "QQuickWebEngineSettings.javascriptCanOpenWindowsChanged() --> void" - << "QQuickWebEngineSettings.javascriptCanAccessClipboardChanged() --> void" - << "QQuickWebEngineSettings.linksIncludedInFocusChainChanged() --> void" - << "QQuickWebEngineSettings.localStorageEnabledChanged() --> void" - << "QQuickWebEngineSettings.localContentCanAccessRemoteUrlsChanged() --> void" - << "QQuickWebEngineSettings.spatialNavigationEnabledChanged() --> void" - << "QQuickWebEngineSettings.localContentCanAccessFileUrlsChanged() --> void" - << "QQuickWebEngineSettings.hyperlinkAuditingEnabledChanged() --> void" - << "QQuickWebEngineSettings.errorPageEnabledChanged() --> void" - << "QQuickWebEngineSettings.defaultTextEncodingChanged() --> void" - << "QQuickWebEngineCertificateError.ignoreCertificateError() --> void" - << "QQuickWebEngineCertificateError.rejectCertificate() --> void" - << "QQuickWebEngineCertificateError.defer() --> void" - << "QQuickWebEngineCertificateError.url --> QUrl" - << "QQuickWebEngineCertificateError.error --> Error" - << "QQuickWebEngineCertificateError.description --> QString" - << "QQuickWebEngineCertificateError.overridable --> bool" + << "QQuickWebEngineProfile.setCookieStoreClient(QWebEngineCookieStoreClient*) --> void" << "QQuickWebEngineScript.Deferred --> InjectionPoint" << "QQuickWebEngineScript.DocumentReady --> InjectionPoint" << "QQuickWebEngineScript.DocumentCreation --> InjectionPoint" @@ -292,8 +315,38 @@ static QStringList expectedAPI = QStringList() << "QQuickWebEngineScript.setWorldId(ScriptWorldId) --> void" << "QQuickWebEngineScript.setRunOnSubframes(bool) --> void" << "QQuickWebEngineScript.toString() --> QString" + << "QQuickWebEngineSettings.autoLoadImages --> bool" + << "QQuickWebEngineSettings.javascriptEnabled --> bool" + << "QQuickWebEngineSettings.javascriptCanOpenWindows --> bool" + << "QQuickWebEngineSettings.javascriptCanAccessClipboard --> bool" + << "QQuickWebEngineSettings.linksIncludedInFocusChain --> bool" + << "QQuickWebEngineSettings.localStorageEnabled --> bool" + << "QQuickWebEngineSettings.localContentCanAccessRemoteUrls --> bool" + << "QQuickWebEngineSettings.spatialNavigationEnabled --> bool" + << "QQuickWebEngineSettings.localContentCanAccessFileUrls --> bool" + << "QQuickWebEngineSettings.hyperlinkAuditingEnabled --> bool" + << "QQuickWebEngineSettings.errorPageEnabled --> bool" + << "QQuickWebEngineSettings.pluginsEnabled --> bool" + << "QQuickWebEngineSettings.fullScreenSupportEnabled --> bool" + << "QQuickWebEngineSettings.defaultTextEncoding --> QString" + << "QQuickWebEngineSettings.autoLoadImagesChanged() --> void" + << "QQuickWebEngineSettings.javascriptEnabledChanged() --> void" + << "QQuickWebEngineSettings.javascriptCanOpenWindowsChanged() --> void" + << "QQuickWebEngineSettings.javascriptCanAccessClipboardChanged() --> void" + << "QQuickWebEngineSettings.linksIncludedInFocusChainChanged() --> void" + << "QQuickWebEngineSettings.localStorageEnabledChanged() --> void" + << "QQuickWebEngineSettings.localContentCanAccessRemoteUrlsChanged() --> void" + << "QQuickWebEngineSettings.spatialNavigationEnabledChanged() --> void" + << "QQuickWebEngineSettings.localContentCanAccessFileUrlsChanged() --> void" + << "QQuickWebEngineSettings.hyperlinkAuditingEnabledChanged() --> void" + << "QQuickWebEngineSettings.errorPageEnabledChanged() --> void" + << "QQuickWebEngineSettings.pluginsEnabledChanged() --> void" + << "QQuickWebEngineSettings.fullScreenSupportEnabledChanged() --> void" + << "QQuickWebEngineSettings.defaultTextEncodingChanged() --> void" << "QQuickWebEngineFullScreenRequest.toggleOn --> bool" << "QQuickWebEngineFullScreenRequest.accept() --> void" + << "QQuickWebEngineSingleton.settings --> QQuickWebEngineSettings*" + << "QQuickWebEngineSingleton.defaultProfile --> QQuickWebEngineProfile*" ; static bool isCheckedEnum(const QByteArray &typeName) diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml index a97739404..e2c5c9009 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -41,12 +41,13 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebEngine.experimental 1.0 WebEngineView { property var loadStatus: null property var viewportReady: false + property bool windowCloseRequestedSignalEmitted: false function waitForLoadSucceeded() { var success = _waitFor(function() { return loadStatus == WebEngineView.LoadSucceededStatus }) @@ -69,6 +70,9 @@ WebEngineView { loadStatus = null return stop } + function waitForWindowCloseRequested() { + return _waitFor(function() { return windowCloseRequestedSignalEmitted; }); + } function _waitFor(predicate) { var timeout = 5000 var i = 0 @@ -87,5 +91,8 @@ WebEngineView { viewportReady = false } + onWindowCloseRequested: { + windowCloseRequestedSignalEmitted = true; + } } diff --git a/tests/auto/quick/qmltests/data/confirmclose.html b/tests/auto/quick/qmltests/data/confirmclose.html new file mode 100644 index 000000000..ba11da7a4 --- /dev/null +++ b/tests/auto/quick/qmltests/data/confirmclose.html @@ -0,0 +1,5 @@ +<html> +<body onbeforeunload="return 'You are about to miss out on some awesome content.';"> + Be greeted, precious viewer! +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/directoryupload.html b/tests/auto/quick/qmltests/data/directoryupload.html new file mode 100644 index 000000000..6a6e4580c --- /dev/null +++ b/tests/auto/quick/qmltests/data/directoryupload.html @@ -0,0 +1,16 @@ +<html> +<head> +<meta name="viewport" initial-scale=1"> +<title> Directory Upload </title> +<script src = "./titleupdate.js"> +</script> + +<body> +<input type="file" id="upfile" webkitdirectory="" directory="" onchange="updateTitle()"> +<script> +window.onload = function() { +document.getElementById("upfile").focus() +} +</script> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/forms.html b/tests/auto/quick/qmltests/data/forms.html new file mode 100644 index 000000000..8dc3472f2 --- /dev/null +++ b/tests/auto/quick/qmltests/data/forms.html @@ -0,0 +1,40 @@ +<html> +<head> + <title>Forms</title> + <script type="text/javascript"> + function updateFocus() { + var name = window.location.hash.substring(1); + var element = document.getElementsByName(name)[0]; + + element.focus(); + } + </script> +</head> +<body onload="updateFocus();"> + <form> + <input type="url" required/> + <input type="submit" name="url_empty"/> + </form> + <form> + <input type="url" value="invalid" required/> + <input type="submit" name="url_invalid"/> + </form> + <form> + <input type="url" value="invalid" title="url_title" required/> + <input type="submit" name="url_title"/> + </form> + + <form> + <input type="email" required/> + <input type="submit" name="email_empty"/> + </form> + <form> + <input type="email" value="invalid" required/> + <input type="submit" name="email_invalid"/> + </form> + <form> + <input type="email" value="invalid" title="email_title" required/> + <input type="submit" name="email_title"/> + </form> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/multifileupload.html b/tests/auto/quick/qmltests/data/multifileupload.html new file mode 100644 index 000000000..cc87d8f41 --- /dev/null +++ b/tests/auto/quick/qmltests/data/multifileupload.html @@ -0,0 +1,17 @@ +<html> +<head> +<meta name="viewport" initial-scale=1"> +<title> Mutli-file Upload </title> +<script src = "./titleupdate.js"> +</script> + +<body> +<input type="file" name="file" id="upfile" onchange="updateTitle()" multiple/> + +<script> +window.onload = function() { +document.getElementById("upfile").focus() +} +</script> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/singlefileupload.html b/tests/auto/quick/qmltests/data/singlefileupload.html new file mode 100644 index 000000000..8469aa128 --- /dev/null +++ b/tests/auto/quick/qmltests/data/singlefileupload.html @@ -0,0 +1,17 @@ +<html> +<head> +<meta name="viewport" initial-scale=1"> +<title> Single File Upload </title> +<script src = "./titleupdate.js"> +</script> + +<body> +<input type="file" name="file" id="upfile" onchange="updateTitle()"/> + +<script> +window.onload = function() { +document.getElementById("upfile").focus() +} +</script> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/titleupdate.js b/tests/auto/quick/qmltests/data/titleupdate.js new file mode 100644 index 000000000..cfcc52c60 --- /dev/null +++ b/tests/auto/quick/qmltests/data/titleupdate.js @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function updateTitle() +{ + var inp = document.getElementById("upfile"); + var allfiles = new String(""); + var name = new String(""); + for (var i = 0; i < inp.files.length; ++i) { + name = inp.files.item(i).name; + if (allfiles.length == 0) + allfiles = name; + else + allfiles = allfiles + "," + name; + } + document.title = allfiles; +} diff --git a/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml new file mode 100644 index 000000000..eaca8822b --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtTest 1.0 + +Item { + id: root + width: 300 + height: 400 + TextInput { + id: textInput + anchors { + top: parent.top + left: parent.left + right: parent.right + } + focus: true + text: "foo" + } + + TestWebEngineView { + id: webEngineView + activeFocusOnPress: false + anchors { + top: textInput.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + TestCase { + name: "ActiveFocusOnPress" + when:windowShown + + function test_activeFocusOnPress() { + textInput.forceActiveFocus() + verify(textInput.activeFocus) + mouseClick(root, 150, 300, Qt.LeftButton) + verify(textInput.activeFocus) + } + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml index dfb983c43..51c1d5580 100644 --- a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_favIconLoad.qml b/tests/auto/quick/qmltests/data/tst_favIconLoad.qml index 73190f1bd..df5479eec 100644 --- a/tests/auto/quick/qmltests/data/tst_favIconLoad.qml +++ b/tests/auto/quick/qmltests/data/tst_favIconLoad.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_filePicker.qml b/tests/auto/quick/qmltests/data/tst_filePicker.qml new file mode 100644 index 000000000..02b2dd024 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.2 +import "../mock-delegates/TestParams" 1.0 + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + + SignalSpy { + id: titleSpy + target: webEngineView + signalName: "titleChanged" + } + + TestCase { + name: "WebEngineViewSingleFileUpload" + when: windowShown + + function init() { + FilePickerParams.filePickerOpened = false + FilePickerParams.selectFiles = false + FilePickerParams.selectedFilesUrl = [] + titleSpy.clear() + } + + // FIXME: Almost every second url loading progress does get stuck at about 90 percent, so the loadFinished signal won't arrive. + // This cleanup function is a workaround for this problem. + function cleanup() { + webEngineView.url = Qt.resolvedUrl("about:blank") + webEngineView.waitForLoadSucceeded() + } + + function test_acceptSingleFileSelection() { + webEngineView.url = Qt.resolvedUrl("singlefileupload.html") + verify(webEngineView.waitForLoadSucceeded()) + + FilePickerParams.selectFiles = true + FilePickerParams.selectedFilesUrl.push(Qt.resolvedUrl("test1.html")) + + keyPress(Qt.Key_Enter) // Focus is on the button. Open FileDialog. + wait(100) // The ui delegate is invoked asynchronously + verify(FilePickerParams.filePickerOpened) + titleSpy.wait() + compare(webEngineView.title, "test1.html") + } + + function test_acceptMultipleFilesSelection() { + webEngineView.url = Qt.resolvedUrl("multifileupload.html") + verify(webEngineView.waitForLoadSucceeded()) + + FilePickerParams.selectFiles = true + FilePickerParams.selectedFilesUrl.push(Qt.resolvedUrl("test1.html")) + FilePickerParams.selectedFilesUrl.push(Qt.resolvedUrl("test2.html")) + + keyPress(Qt.Key_Enter) // Focus is on the button. Open FileDialog. + wait(100) + verify(FilePickerParams.filePickerOpened) + titleSpy.wait() + compare(webEngineView.title, "test1.html,test2.html") + } + + function test_acceptDirectory() { + webEngineView.url = Qt.resolvedUrl("directoryupload.html") + verify(webEngineView.waitForLoadSucceeded()) + + FilePickerParams.selectFiles = true + FilePickerParams.selectedFilesUrl.push(Qt.resolvedUrl("../data")) + + keyPress(Qt.Key_Enter) // Focus is on the button. Open FileDialog. + wait(100) // The ui delegate is invoked asynchronously + verify(FilePickerParams.filePickerOpened) + titleSpy.wait() + compare(webEngineView.title, "data") + } + + function test_reject() { + webEngineView.url = Qt.resolvedUrl("singlefileupload.html") + verify(webEngineView.waitForLoadSucceeded()) + + titleSpy.clear() + keyPress(Qt.Key_Enter) // Focus is on the button. Open FileDialog. + wait(100) + compare(titleSpy.count, 0) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index b51da0b2e..9c4aa48c1 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_formValidation.qml b/tests/auto/quick/qmltests/data/tst_formValidation.qml new file mode 100644 index 000000000..4acb7ce63 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_formValidation.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.2 +import QtWebEngine.testsupport 1.0 + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + + testSupport: WebEngineTestSupport { + id: testSupportAPI + } + + SignalSpy { + id: showSpy + target: testSupportAPI + signalName: "validationMessageShown" + } + + TestCase { + name: "WebEngineViewFormValidation" + when: windowShown + + function init() { + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + showSpy.clear() + } + + function test_urlForm() { + webEngineView.url = Qt.resolvedUrl("forms.html#url_empty") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[0][0], "Please fill out this field.") + + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.url = Qt.resolvedUrl("forms.html#url_invalid") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[1][0], "Please enter a URL.") + + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.url = Qt.resolvedUrl("forms.html#url_title") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[2][1], "url_title") + } + + function test_emailForm() { + webEngineView.url = Qt.resolvedUrl("forms.html#email_empty") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[0][0], "Please fill out this field.") + + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.url = Qt.resolvedUrl("forms.html#email_invalid") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[1][0], "Please include an '@' in the email address. 'invalid' is missing an '@'.") + + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.url = Qt.resolvedUrl("forms.html#email_title") + verify(webEngineView.waitForLoadSucceeded()) + keyPress(Qt.Key_Enter) + showSpy.wait() + compare(showSpy.signalArguments[2][1], "email_title") + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml index 58a49da5a..4294c5ba3 100644 --- a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml +++ b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml @@ -41,12 +41,27 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.0 +import QtWebEngine 1.2 +import QtWebEngine.testsupport 1.0 import "../mock-delegates/TestParams" 1.0 TestWebEngineView { id: webEngineView + testSupport: WebEngineTestSupport { + property bool windowCloseRejectedSignalEmitted: false + + function waitForWindowCloseRejected() { + return _waitFor(function () { + return testSupport.windowCloseRejectedSignalEmitted; + }); + } + + onWindowCloseRejected: { + windowCloseRejectedSignalEmitted = true; + } + } + TestCase { id: test name: "WebEngineViewJavaScriptDialogs" @@ -80,6 +95,24 @@ TestWebEngineView { } + function test_confirmClose() { + webEngineView.url = Qt.resolvedUrl("confirmclose.html"); + verify(webEngineView.waitForLoadSucceeded()); + webEngineView.windowCloseRequestedSignalEmitted = false; + JSDialogParams.shouldAcceptDialog = true; + webEngineView.triggerWebAction(WebEngineView.RequestClose); + verify(webEngineView.waitForWindowCloseRequested()); + } + + function test_rejectClose() { + webEngineView.url = Qt.resolvedUrl("confirmclose.html"); + verify(webEngineView.waitForLoadSucceeded()); + webEngineView.testSupport.windowCloseRejectedSignalEmitted = false; + JSDialogParams.shouldAcceptDialog = false; + webEngineView.triggerWebAction(WebEngineView.RequestClose); + verify(webEngineView.testSupport.waitForWindowCloseRejected()); + } + function test_prompt() { JSDialogParams.inputForPrompt = "tQ olleH" webEngineView.url = Qt.resolvedUrl("prompt.html") diff --git a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml index 230ee9635..c127d7391 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_linkHovered.qml b/tests/auto/quick/qmltests/data/tst_linkHovered.qml index c9fbd5520..31d90615b 100644 --- a/tests/auto/quick/qmltests/data/tst_linkHovered.qml +++ b/tests/auto/quick/qmltests/data/tst_linkHovered.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadFail.qml b/tests/auto/quick/qmltests/data/tst_loadFail.qml index 0885fc193..c2a4b6e13 100644 --- a/tests/auto/quick/qmltests/data/tst_loadFail.qml +++ b/tests/auto/quick/qmltests/data/tst_loadFail.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebEngine.experimental 1.0 import QtWebEngine.testsupport 1.0 diff --git a/tests/auto/quick/qmltests/data/tst_loadHtml.qml b/tests/auto/quick/qmltests/data/tst_loadHtml.qml index b8acd0dd7..ee1149b16 100644 --- a/tests/auto/quick/qmltests/data/tst_loadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_loadHtml.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadProgress.qml b/tests/auto/quick/qmltests/data/tst_loadProgress.qml index 9f8b6f6f7..096861c4d 100644 --- a/tests/auto/quick/qmltests/data/tst_loadProgress.qml +++ b/tests/auto/quick/qmltests/data/tst_loadProgress.qml @@ -41,22 +41,39 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView width: 400 height: 300 + property var loadProgressArray: [] + + onLoadProgressChanged: { + loadProgressArray.push(webEngineView.loadProgress) + } + TestCase { name: "WebEngineViewLoadProgress" function test_loadProgress() { compare(webEngineView.loadProgress, 0) + loadProgressArray = [] + webEngineView.url = Qt.resolvedUrl("test1.html") - compare(webEngineView.loadProgress, 0) verify(webEngineView.waitForLoadSucceeded()) - compare(webEngineView.loadProgress, 100) + + // Test whether the chromium emits progress numbers in ascending order + var loadProgressMin = 0 + for (var i in loadProgressArray) { + var loadProgress = loadProgressArray[i] + verify(loadProgressMin <= loadProgress) + loadProgressMin = loadProgress + } + + // The progress must be 100% at the end + compare(loadProgressArray[loadProgressArray.length - 1], 100) } } } diff --git a/tests/auto/quick/qmltests/data/tst_loadProgressSignal.qml b/tests/auto/quick/qmltests/data/tst_loadProgressSignal.qml index 8e2e99b64..7b0bac61b 100644 --- a/tests/auto/quick/qmltests/data/tst_loadProgressSignal.qml +++ b/tests/auto/quick/qmltests/data/tst_loadProgressSignal.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml index 2400a5ed6..fb692c472 100644 --- a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml +++ b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml @@ -41,7 +41,7 @@ import QtQuick 2.3 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 Item { width: 300 diff --git a/tests/auto/quick/qmltests/data/tst_loadUrl.qml b/tests/auto/quick/qmltests/data/tst_loadUrl.qml index 922925b48..c8abf2bb0 100644 --- a/tests/auto/quick/qmltests/data/tst_loadUrl.qml +++ b/tests/auto/quick/qmltests/data/tst_loadUrl.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebEngine.experimental 1.0 TestWebEngineView { diff --git a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml index 3acde3abc..f7875bb78 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml index 72eb0aac9..7d49cda90 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_properties.qml b/tests/auto/quick/qmltests/data/tst_properties.qml index 738ef532d..9418252cb 100644 --- a/tests/auto/quick/qmltests/data/tst_properties.qml +++ b/tests/auto/quick/qmltests/data/tst_properties.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml index 6cf3a71fb..07e7130c6 100644 --- a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml +++ b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_titleChanged.qml b/tests/auto/quick/qmltests/data/tst_titleChanged.qml index adc8564c0..8d9dae0a4 100644 --- a/tests/auto/quick/qmltests/data/tst_titleChanged.qml +++ b/tests/auto/quick/qmltests/data/tst_titleChanged.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml index 1c32c73a9..5fefd0fe5 100644 --- a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml +++ b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 Item { id: parentItem diff --git a/tests/auto/quick/qmltests/data/tst_userScripts.qml b/tests/auto/quick/qmltests/data/tst_userScripts.qml index a9ed933d9..8a3b8207f 100644 --- a/tests/auto/quick/qmltests/data/tst_userScripts.qml +++ b/tests/auto/quick/qmltests/data/tst_userScripts.qml @@ -41,7 +41,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 Item { WebEngineScript { diff --git a/tests/auto/quick/qmltests/data/tst_webchannel.qml b/tests/auto/quick/qmltests/data/tst_webchannel.qml index dce585b67..51e37d50e 100644 --- a/tests/auto/quick/qmltests/data/tst_webchannel.qml +++ b/tests/auto/quick/qmltests/data/tst_webchannel.qml @@ -40,7 +40,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebEngine.experimental 1.0 import QtWebChannel 1.0 diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/FilePicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/FilePicker.qml new file mode 100644 index 000000000..5ee231c19 --- /dev/null +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/FilePicker.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import "../../TestParams" 1.0 + +QtObject { + property bool selectMultiple: false; + property bool selectExisting: false; + property bool selectFolder: false; + + signal filesSelected(var fileList); + signal rejected(); + + function open() { + FilePickerParams.filePickerOpened = true; + if (FilePickerParams.selectFiles) + filesSelected(FilePickerParams.selectedFilesUrl) + else + rejected() + } +} diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/qmldir b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/qmldir index 1ebabd335..cf8ac0512 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/qmldir +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/UIDelegates/qmldir @@ -1,4 +1,5 @@ module QtWebEngine.UIDelegates AlertDialog 1.0 AlertDialog.qml ConfirmDialog 1.0 ConfirmDialog.qml +FilePicker 1.0 FilePicker.qml PromptDialog 1.0 PromptDialog.qml diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml new file mode 100644 index 000000000..f0f2d9368 --- /dev/null +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +pragma Singleton +import QtQuick 2.0 + +QtObject { + property var selectedFilesUrl: []; + property bool selectFiles: false; + property bool filePickerOpened: false; +} diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/qmldir b/tests/auto/quick/qmltests/mock-delegates/TestParams/qmldir index f2ed87a75..a21dd8236 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/qmldir +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/qmldir @@ -1,4 +1,5 @@ # QML module so that the autotests can set testing parameters module TestParams +singleton FilePickerParams 1.0 FilePickerParams.qml singleton JSDialogParams 1.0 JSDialogParams.qml diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 9f6f55275..57649384d 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -12,23 +12,31 @@ OTHER_FILES += \ $$PWD/data/change-document-title.js \ $$PWD/data/download.zip \ $$PWD/data/confirm.html \ + $$PWD/data/confirmclose.html \ + $$PWD/data/directoryupload.html \ $$PWD/data/favicon.html \ $$PWD/data/favicon.png \ $$PWD/data/favicon2.html \ + $$PWD/data/forms.html \ $$PWD/data/geolocation.html \ $$PWD/data/javascript.html \ $$PWD/data/link.html \ $$PWD/data/prompt.html \ + $$PWD/data/multifileupload.html \ $$PWD/data/redirect.html \ + $$PWD/data/singlefileupload.html \ $$PWD/data/small-favicon.png \ $$PWD/data/test1.html \ $$PWD/data/test2.html \ $$PWD/data/test3.html \ $$PWD/data/test4.html \ $$PWD/data/keyboardModifierMapping.html \ + $$PWD/data/titleupdate.js \ $$PWD/data/tst_desktopBehaviorLoadHtml.qml \ $$PWD/data/tst_download.qml \ $$PWD/data/tst_favIconLoad.qml \ + $$PWD/data/tst_filePicker.qml \ + $$PWD/data/tst_formValidation.qml \ $$PWD/data/tst_geopermission.qml \ $$PWD/data/tst_javaScriptDialogs.qml \ $$PWD/data/tst_linkHovered.qml \ @@ -49,12 +57,13 @@ OTHER_FILES += \ $$PWD/data/tst_keyboardModifierMapping.qml \ $$PWD/mock-delegates/QtWebEngine/UIDelegates/AlertDialog.qml \ $$PWD/mock-delegates/QtWebEngine/UIDelegates/ConfirmDialog.qml \ + $$PWD/mock-delegates/QtWebEngine/UIDelegates/FilePicker.qml \ $$PWD/mock-delegates/QtWebEngine/UIDelegates/PromptDialog.qml \ $$PWD/mock-delegates/QtWebEngine/UIDelegates/qmldir \ + $$PWD/mock-delegates/TestParams/FilePickerParams.qml \ $$PWD/mock-delegates/TestParams/JSDialogParams.qml \ $$PWD/mock-delegates/TestParams/qmldir \ - load(qt_build_paths) DEFINES += QUICK_TEST_SOURCE_DIR=\\\"$$re_escape($$PWD$${QMAKE_DIR_SEP}data)\\\" diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 2cbcf0979..40dc3cb61 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -73,7 +73,7 @@ tst_QQuickWebEngineView::tst_QQuickWebEngineView() static QQmlEngine *engine = new QQmlEngine(this); m_component.reset(new QQmlComponent(engine, this)); m_component->setData(QByteArrayLiteral("import QtQuick 2.0\n" - "import QtWebEngine 1.1\n" + "import QtWebEngine 1.2\n" "WebEngineView {}") , QUrl()); } @@ -335,12 +335,10 @@ void tst_QQuickWebEngineView::titleUpdate() webEngineView()->setUrl(QUrl::fromLocalFile(QLatin1String(TESTS_SOURCE_DIR "html/file_that_does_not_exist.html"))); QVERIFY(waitForLoadFailed(webEngineView())); QCOMPARE(titleSpy.size(), 0); - } void tst_QQuickWebEngineView::transparentWebEngineViews() { - showWebEngineView(); // This should not crash. @@ -348,13 +346,10 @@ void tst_QQuickWebEngineView::transparentWebEngineViews() webEngineView1->setParentItem(m_window->contentItem()); QScopedPointer<QQuickWebEngineView> webEngineView2(newWebEngineView()); webEngineView2->setParentItem(m_window->contentItem()); -#if !defined(QQUICKWEBENGINEVIEW_EXPERIMENTAL_TRANSPARENTBACKGROUND) - QWARN("QQUICKWEBENGINEVIEW_EXPERIMENTAL_TRANSPARENTBACKGROUND"); -#else - QVERIFY(!webEngineView1->experimental()->transparentBackground()); - webEngineView2->experimental()->setTransparentBackground(true); - QVERIFY(webEngineView2->experimental()->transparentBackground()); -#endif + + QVERIFY(webEngineView1->backgroundColor() != Qt::transparent); + webEngineView2->setBackgroundColor(Qt::transparent); + QVERIFY(webEngineView2->backgroundColor() == Qt::transparent); webEngineView1->setSize(QSizeF(300, 400)); webEngineView1->loadHtml("<html><body bgcolor=\"red\"></body></html>"); @@ -367,7 +362,24 @@ void tst_QQuickWebEngineView::transparentWebEngineViews() webEngineView2->setVisible(true); QTest::qWait(200); - // FIXME: test actual rendering results; https://bugs.webkit.org/show_bug.cgi?id=80609. + + // Result image: black text on red background. + QImage grabbedWindow = m_window->grabWindow(); + + QSet<int> redComponents; + for (int i = 0, width = grabbedWindow.width(); i < width; i++) { + for (int j = 0, height = grabbedWindow.height(); j < height; j++) { + QColor color(grabbedWindow.pixel(i, j)); + redComponents.insert(color.red()); + // There are no green or blue components between red and black. + QVERIFY(color.green() == 0); + QVERIFY(color.blue() == 0); + } + } + + QVERIFY(redComponents.count() > 1); + QVERIFY(redComponents.contains(0)); // black + QVERIFY(redComponents.contains(255)); // red } void tst_QQuickWebEngineView::inputMethod() diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp index 5d15537b3..e0e876fc8 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp +++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp @@ -184,7 +184,7 @@ void tst_QQuickWebEngineViewGraphics::reparentToOtherWindow() void tst_QQuickWebEngineViewGraphics::setHtml(const QString &html) { QString htmlData = QUrl::toPercentEncoding(html); - QString qmlData = QUrl::toPercentEncoding(QStringLiteral("import QtQuick 2.0; import QtWebEngine 1.1; WebEngineView { width: 150; height: 150; url: loadUrl }")); + QString qmlData = QUrl::toPercentEncoding(QStringLiteral("import QtQuick 2.0; import QtWebEngine 1.2; WebEngineView { width: 150; height: 150; url: loadUrl }")); m_view->rootContext()->setContextProperty("loadUrl", QUrl(QStringLiteral("data:text/html,%1").arg(htmlData))); m_view->setSource(QUrl(QStringLiteral("data:text/plain,%1").arg(qmlData))); m_view->create(); diff --git a/tests/auto/widgets/qwebengineaccessibility/qwebengineaccessibility.pro b/tests/auto/widgets/qwebengineaccessibility/qwebengineaccessibility.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebengineaccessibility/qwebengineaccessibility.pro +++ b/tests/auto/widgets/qwebengineaccessibility/qwebengineaccessibility.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebengineframe/qwebengineframe.pro b/tests/auto/widgets/qwebengineframe/qwebengineframe.pro deleted file mode 100644 index ff6c49628..000000000 --- a/tests/auto/widgets/qwebengineframe/qwebengineframe.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.cpp deleted file mode 100644 index 00e1b1123..000000000 --- a/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.cpp +++ /dev/null @@ -1,1654 +0,0 @@ -/* - Copyright (C) 2008,2009 Nokia Corporation and/or its subsidiary(-ies) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include <QtTest/QtTest> - -#include <qwebenginepage.h> -#include <qwebengineview.h> -#include <qwebenginehistory.h> -#include <QAbstractItemView> -#include <QApplication> -#include <QComboBox> -#include <QPaintEngine> -#include <QPicture> -#include <QRegExp> -#include <QNetworkRequest> -#include <QNetworkReply> -#include <QTextCodec> -#include <QWebEngineSettings> -#ifndef QT_NO_OPENSSL -#include <qsslerror.h> -#endif -#include "../util.h" - -class tst_QWebEngineFrame : public QObject -{ - Q_OBJECT - -public: - bool eventFilter(QObject* watched, QEvent* event); - -public Q_SLOTS: - void initTestCase(); - void init(); - void cleanup(); - -private Q_SLOTS: - void horizontalScrollAfterBack(); - void symmetricUrl(); - void progressSignal(); - void urlChange(); - void requestedUrl(); - void requestedUrlAfterSetAndLoadFailures(); - void javaScriptWindowObjectCleared_data(); - void javaScriptWindowObjectCleared(); - void javaScriptWindowObjectClearedOnEvaluate(); - void asyncAndDelete(); - void earlyToHtml(); - void setHtml(); - void setHtmlWithImageResource(); - void setHtmlWithStylesheetResource(); - void setHtmlWithBaseURL(); - void setHtmlWithJSAlert(); - void ipv6HostEncoding(); - void metaData(); -#if !defined(QT_NO_COMBOBOX) - void popupFocus(); -#endif - void inputFieldFocus(); - void hitTestContent(); - void baseUrl_data(); - void baseUrl(); - void hasSetFocus(); - void renderGeometry(); - void renderHints(); - void scrollPosition(); - void scrollToAnchor(); - void scrollbarsOff(); - void evaluateWillCauseRepaint(); - void setContent_data(); - void setContent(); - void setCacheLoadControlAttribute(); - void setUrlWithPendingLoads(); - void setUrlToEmpty(); - void setUrlToInvalid(); - void setUrlHistory(); - void setUrlUsingStateObject(); - void setUrlSameUrl(); - void setUrlThenLoads_data(); - void setUrlThenLoads(); - void loadFinishedAfterNotFoundError(); - void loadInSignalHandlers_data(); - void loadInSignalHandlers(); - -private: - QWebEngineView* m_view; - QWebEnginePage* m_page; - QWebEngineView* m_inputFieldsTestView; - int m_inputFieldTestPaintCount; -}; - -bool tst_QWebEngineFrame::eventFilter(QObject* watched, QEvent* event) -{ - // used on the inputFieldFocus test - if (watched == m_inputFieldsTestView) { - if (event->type() == QEvent::Paint) - m_inputFieldTestPaintCount++; - } - return QObject::eventFilter(watched, event); -} - -void tst_QWebEngineFrame::initTestCase() -{ -} - -void tst_QWebEngineFrame::init() -{ - m_view = new QWebEngineView(); - m_page = m_view->page(); - m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); -} - -void tst_QWebEngineFrame::cleanup() -{ - delete m_view; -} - -void tst_QWebEngineFrame::symmetricUrl() -{ - QWebEngineView view; - QSignalSpy loadFinishedSpy(view.page(), SIGNAL(loadFinished(bool))); - - QVERIFY(view.url().isEmpty()); - - QCOMPARE(view.history()->count(), 0); - - QUrl dataUrl("data:text/html,<h1>Test"); - - view.setUrl(dataUrl); - QCOMPARE(view.url(), dataUrl); - QCOMPARE(view.history()->count(), 0); - - // loading is _not_ immediate, so the text isn't set just yet. - QVERIFY(toPlainTextSync(view.page()).isEmpty()); - - QTRY_COMPARE(loadFinishedSpy.count(), 1); - - QCOMPARE(view.history()->count(), 1); - QCOMPARE(toPlainTextSync(view.page()), QString("Test")); - - QUrl dataUrl2("data:text/html,<h1>Test2"); - QUrl dataUrl3("data:text/html,<h1>Test3"); - - view.setUrl(dataUrl2); - view.setUrl(dataUrl3); - - QCOMPARE(view.url(), dataUrl3); - - QTRY_VERIFY(loadFinishedSpy.count() >= 2); - QTRY_COMPARE(loadFinishedSpy.count(), 3); - - QCOMPARE(view.history()->count(), 2); - - QCOMPARE(toPlainTextSync(view.page()), QString("Test3")); -} - -void tst_QWebEngineFrame::progressSignal() -{ - QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); - - QUrl dataUrl("data:text/html,<h1>Test"); - m_view->setUrl(dataUrl); - - ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); - - QVERIFY(progressSpy.size() >= 2); - int previousValue = -1; - for (QSignalSpy::ConstIterator it = progressSpy.begin(); it < progressSpy.end(); ++it) { - int current = (*it).first().toInt(); - QVERIFY(current > previousValue); - previousValue = current; - } - - // But we always end at 100% - QCOMPARE(progressSpy.last().first().toInt(), 100); -} - -void tst_QWebEngineFrame::urlChange() -{ - QSignalSpy urlSpy(m_page, SIGNAL(urlChanged(QUrl))); - - QUrl dataUrl("data:text/html,<h1>Test"); - m_view->setUrl(dataUrl); - - ::waitForSignal(m_page, SIGNAL(urlChanged(QUrl))); - - QCOMPARE(urlSpy.size(), 1); - - QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); - m_view->setUrl(dataUrl2); - - ::waitForSignal(m_page, SIGNAL(urlChanged(QUrl))); - - QCOMPARE(urlSpy.size(), 2); -} - -class FakeReply : public QNetworkReply { - Q_OBJECT - -public: - static const QUrl urlFor404ErrorWithoutContents; - - FakeReply(const QNetworkRequest& request, QObject* parent = 0) - : QNetworkReply(parent) - { - setOperation(QNetworkAccessManager::GetOperation); - setRequest(request); - setUrl(request.url()); - if (request.url() == QUrl("qrc:/test1.html")) { - setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); - setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); - QTimer::singleShot(0, this, SLOT(continueRedirect())); - } -#ifndef QT_NO_OPENSSL - else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) { - setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error!")); - QTimer::singleShot(0, this, SLOT(continueError())); - } -#endif - else if (request.url().host() == QLatin1String("abcdef.abcdef")) { - setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); - QTimer::singleShot(0, this, SLOT(continueError())); - } else if (request.url() == FakeReply::urlFor404ErrorWithoutContents) { - setError(QNetworkReply::ContentNotFoundError, "Not found"); - setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 404); - QTimer::singleShot(0, this, SLOT(continueError())); - } - - open(QIODevice::ReadOnly); - } - ~FakeReply() - { - close(); - } - virtual void abort() {} - virtual void close() {} - -protected: - qint64 readData(char*, qint64) - { - return 0; - } - -private Q_SLOTS: - void continueRedirect() - { - emit metaDataChanged(); - emit finished(); - } - - void continueError() - { - emit error(this->error()); - emit finished(); - } -}; - -const QUrl FakeReply::urlFor404ErrorWithoutContents = QUrl("http://this.will/return-http-404-error-without-contents.html"); - -class FakeNetworkManager : public QNetworkAccessManager { - Q_OBJECT - -public: - FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } - -protected: - virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) - { - QString url = request.url().toString(); - if (op == QNetworkAccessManager::GetOperation) { -#ifndef QT_NO_OPENSSL - if (url == "qrc:/fake-ssl-error.html") { - FakeReply* reply = new FakeReply(request, this); - QList<QSslError> errors; - emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); - return reply; - } -#endif - if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/" || request.url() == FakeReply::urlFor404ErrorWithoutContents) - return new FakeReply(request, this); - } - - return QNetworkAccessManager::createRequest(op, request, outgoingData); - } -}; - -void tst_QWebEngineFrame::requestedUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QWebEnginePage page; - - // in few seconds, the image should be completely loaded - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - FakeNetworkManager* networkManager = new FakeNetworkManager(&page); - page.setNetworkAccessManager(networkManager); - - page.setUrl(QUrl("qrc:/test1.html")); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(spy.count(), 1); - QCOMPARE(page.requestedUrl(), QUrl("qrc:/test1.html")); - QCOMPARE(page.url(), QUrl("qrc:/test2.html")); - - page.setUrl(QUrl("qrc:/non-existent.html")); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(spy.count(), 2); - QCOMPARE(page.requestedUrl(), QUrl("qrc:/non-existent.html")); - QCOMPARE(page.url(), QUrl("qrc:/non-existent.html")); - - page.setUrl(QUrl("http://abcdef.abcdef")); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(spy.count(), 3); - QCOMPARE(page.requestedUrl(), QUrl("http://abcdef.abcdef/")); - QCOMPARE(page.url(), QUrl("http://abcdef.abcdef/")); - -#ifndef QT_NO_OPENSSL - qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); - qRegisterMetaType<QNetworkReply* >("QNetworkReply*"); - - QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); - page.setUrl(QUrl("qrc:/fake-ssl-error.html")); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(spy2.count(), 1); - QCOMPARE(page.requestedUrl(), QUrl("qrc:/fake-ssl-error.html")); - QCOMPARE(page.url(), QUrl("qrc:/fake-ssl-error.html")); -#endif -#endif -} - -void tst_QWebEngineFrame::requestedUrlAfterSetAndLoadFailures() -{ - QWebEnginePage page; - page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - - const QUrl first("http://abcdef.abcdef/"); - page.setUrl(first); - ::waitForSignal(&page, SIGNAL(loadFinished(bool))); - QCOMPARE(spy.count(), 1); - QCOMPARE(page.url(), first); - QCOMPARE(page.requestedUrl(), first); - QVERIFY(!spy.at(0).first().toBool()); - - const QUrl second("http://abcdef.abcdef/another_page.html"); - QVERIFY(first != second); - - page.load(second); - ::waitForSignal(&page, SIGNAL(loadFinished(bool))); - QCOMPARE(spy.count(), 2); - QCOMPARE(page.url(), first); - QCOMPARE(page.requestedUrl(), second); - QVERIFY(!spy.at(1).first().toBool()); -} - -void tst_QWebEngineFrame::javaScriptWindowObjectCleared_data() -{ - QTest::addColumn<QString>("html"); - QTest::addColumn<int>("signalCount"); - QTest::newRow("with <script>") << "<html><body><script>i=0</script><p>hello world</p></body></html>" << 1; - // NOTE: Empty scripts no longer cause this signal to be emitted. - QTest::newRow("with empty <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 0; - QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; -} - -void tst_QWebEngineFrame::javaScriptWindowObjectCleared() -{ -#if !defined(QWEBENGINEPAGE_JAVASCRIPTWINDOWOBJECTCLEARED) - QSKIP("QWEBENGINEPAGE_JAVASCRIPTWINDOWOBJECTCLEARED"); -#else - QWebEnginePage page; - QSignalSpy spy(&page, SIGNAL(javaScriptWindowObjectCleared())); - QFETCH(QString, html); - page.setHtml(html); - - QFETCH(int, signalCount); - QCOMPARE(spy.count(), signalCount); -#endif -} - -void tst_QWebEngineFrame::javaScriptWindowObjectClearedOnEvaluate() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - QWebEnginePage page; - QSignalSpy spy(&page, SIGNAL(javaScriptWindowObjectCleared())); - page.setHtml("<html></html>"); - QCOMPARE(spy.count(), 0); - page.evaluateJavaScript("var a = 'a';"); - QCOMPARE(spy.count(), 1); - // no new clear for a new script: - page.evaluateJavaScript("var a = 1;"); - QCOMPARE(spy.count(), 1); -#endif -} - -void tst_QWebEngineFrame::asyncAndDelete() -{ - QWebEnginePage *page = new QWebEnginePage; - CallbackSpy<QString> plainTextSpy; - CallbackSpy<QString> htmlSpy; - page->toPlainText(plainTextSpy.ref()); - page->toHtml(htmlSpy.ref()); - - delete page; - // Pending callbacks should be called with an empty value in the page's destructor. - QCOMPARE(plainTextSpy.waitForResult(), QString()); - QVERIFY(plainTextSpy.wasCalled()); - QCOMPARE(htmlSpy.waitForResult(), QString()); - QVERIFY(htmlSpy.wasCalled()); -} - -void tst_QWebEngineFrame::earlyToHtml() -{ - QString html("<html><head></head><body></body></html>"); - QCOMPARE(toHtmlSync(m_view->page()), html); -} - -void tst_QWebEngineFrame::setHtml() -{ - QString html("<html><head></head><body><p>hello world</p></body></html>"); - QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool))); - m_view->page()->setHtml(html); - QVERIFY(spy.wait()); - QCOMPARE(toHtmlSync(m_view->page()), html); -} - -void tst_QWebEngineFrame::setHtmlWithImageResource() -{ - // By default, only security origins of local files can load local resources. - // So we should specify baseUrl to be a local file in order to get a proper origin and load the local image. - - QLatin1String html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>"); - QWebEnginePage page; - - page.setHtml(html, QUrl(QLatin1String("file:///path/to/file"))); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - - QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); - - // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. - - page.setHtml(html); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 0); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 0); -} - -void tst_QWebEngineFrame::setHtmlWithStylesheetResource() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - // By default, only security origins of local files can load local resources. - // So we should specify baseUrl to be a local file in order to be able to download the local stylesheet. - - const char* htmlData = - "<html>" - "<head>" - "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" - "</head>" - "<body>" - "<p id='idP'>some text</p>" - "</body>" - "</html>"; - QLatin1String html(htmlData); - QWebEnginePage page; - QWebEngineElement webElement; - - page.setHtml(html, QUrl(QLatin1String("qrc:///file"))); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - webElement = page.documentElement().findFirst("p"); - QCOMPARE(webElement.styleProperty("color", QWebEngineElement::CascadedStyle), QLatin1String("red")); - - // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. - - page.setHtml(html, QUrl(QLatin1String("http://www.example.com/"))); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - webElement = page.documentElement().findFirst("p"); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); - QCOMPARE(webElement.styleProperty("color", QWebEngineElement::CascadedStyle), QString()); -#endif -} - -void tst_QWebEngineFrame::setHtmlWithBaseURL() -{ - // This tests if baseUrl is indeed affecting the relative paths from resources. - // As we are using a local file as baseUrl, its security origin should be able to load local resources. - - if (!QDir(TESTS_SOURCE_DIR).exists()) - W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); - - QDir::setCurrent(TESTS_SOURCE_DIR); - - QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); - - QWebEnginePage page; - - // in few seconds, the image should be completey loaded - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - - page.setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); - waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(spy.count(), 1); - - QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); - QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); - - // no history item has to be added. - QCOMPARE(m_view->page()->history()->count(), 0); -} - -class MyPage : public QWebEnginePage -{ -public: - MyPage() : QWebEnginePage(), alerts(0) {} - int alerts; - -protected: - virtual void javaScriptAlert(const QUrl &securityOrigin, const QString &msg) - { - alerts++; - QCOMPARE(securityOrigin, QUrl(QStringLiteral("http://test.origin.com/"))); - QCOMPARE(msg, QString("foo")); - } -}; - -void tst_QWebEngineFrame::setHtmlWithJSAlert() -{ - QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>"); - MyPage page; - page.setHtml(html, QUrl(QStringLiteral("http://test.origin.com/path#fragment"))); - waitForSignal(&page, SIGNAL(loadFinished(bool))); - QCOMPARE(page.alerts, 1); - QCOMPARE(toHtmlSync(&page), html); -} - -class TestNetworkManager : public QNetworkAccessManager -{ -public: - TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} - - QList<QUrl> requestedUrls; - -protected: - virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { - requestedUrls.append(request.url()); - QNetworkRequest redirectedRequest = request; - redirectedRequest.setUrl(QUrl("data:text/html,<p>hello")); - return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData); - } -}; - -void tst_QWebEngineFrame::ipv6HostEncoding() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - networkManager->requestedUrls.clear(); - - QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html"); - m_view->setHtml("<p>Hi", baseUrl); - m_view->page()->evaluateJavaScript("var r = new XMLHttpRequest();" - "r.open('GET', 'http://[::1]/test.xml', false);" - "r.send(null);" - ); - QCOMPARE(networkManager->requestedUrls.count(), 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); -#endif -} - -void tst_QWebEngineFrame::metaData() -{ -#if !defined(QWEBENGINEPAGE_METADATA) - QSKIP("QWEBENGINEPAGE_METADATA"); -#else - m_view->setHtml("<html>" - " <head>" - " <meta name=\"description\" content=\"Test description\">" - " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" - " </head>" - "</html>"); - - QMultiMap<QString, QString> metaData = m_view->page()->metaData(); - - QCOMPARE(metaData.count(), 2); - - QCOMPARE(metaData.value("description"), QString("Test description")); - QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); - QCOMPARE(metaData.value("nonexistent"), QString()); - - m_view->setHtml("<html>" - " <head>" - " <meta name=\"samekey\" content=\"FirstValue\">" - " <meta name=\"samekey\" content=\"SecondValue\">" - " </head>" - "</html>"); - - metaData = m_view->page()->metaData(); - - QCOMPARE(metaData.count(), 2); - - QStringList values = metaData.values("samekey"); - QCOMPARE(values.count(), 2); - - QVERIFY(values.contains("FirstValue")); - QVERIFY(values.contains("SecondValue")); - - QCOMPARE(metaData.value("nonexistent"), QString()); -#endif -} - -#if !defined(QT_NO_COMBOBOX) -void tst_QWebEngineFrame::popupFocus() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - view.setHtml("<html>" - " <body>" - " <select name=\"select\">" - " <option>1</option>" - " <option>2</option>" - " </select>" - " <input type=\"text\"> </input>" - " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" - "This test checks whether showing and hiding a popup" - "takes the focus away from the webpage." - " </textarea>" - " </body>" - "</html>"); - view.resize(400, 100); - // Call setFocus before show to work around http://bugreports.qt.nokia.com/browse/QTBUG-14762 - view.setFocus(); - view.show(); - QTest::qWaitForWindowExposed(&view); - view.activateWindow(); - QTRY_VERIFY(view.hasFocus()); - - // open the popup by clicking. check if focus is on the popup - const QWebEngineElement webCombo = view.page()->documentElement().findFirst(QLatin1String("select[name=select]")); - QTest::mouseClick(&view, Qt::LeftButton, 0, webCombo.geometry().center()); - - QComboBox* combo = view.findChild<QComboBox*>(); - QVERIFY(combo != 0); - QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup - - // hide the popup and check if focus is on the page - combo->hidePopup(); - QTRY_VERIFY(view.hasFocus()); // Focus should be back on the WebView -#endif -} -#endif - -void tst_QWebEngineFrame::inputFieldFocus() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - view.setHtml("<html><body><input type=\"text\"></input></body></html>"); - view.resize(400, 100); - view.show(); - QTest::qWaitForWindowExposed(&view); - view.activateWindow(); - view.setFocus(); - QTRY_VERIFY(view.hasFocus()); - - // double the flashing time, should at least blink once already - int delay = qApp->cursorFlashTime() * 2; - - // focus the lineedit and check if it blinks - bool autoSipEnabled = qApp->autoSipEnabled(); - qApp->setAutoSipEnabled(false); - const QWebEngineElement inputElement = view.page()->documentElement().findFirst(QLatin1String("input[type=text]")); - QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); - m_inputFieldsTestView = &view; - view.installEventFilter( this ); - QTest::qWait(delay); - QVERIFY2(m_inputFieldTestPaintCount >= 3, - "The input field should have a blinking caret"); - qApp->setAutoSipEnabled(autoSipEnabled); -#endif -} - -void tst_QWebEngineFrame::hitTestContent() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); - - QWebEnginePage page; - page.setHtml(html); - page.setViewportSize(QSize(200, 0)); //no height so link is not visible - const QWebEngineElement linkElement = page.documentElement().findFirst(QLatin1String("a#link")); - QWebEngineHitTestResult result = page.hitTestContent(linkElement.geometry().center()); - QCOMPARE(result.linkText(), QString("link text")); - QWebEngineElement link = result.linkElement(); - QCOMPARE(link.attribute("target"), QString("_foo")); -#endif -} - -void tst_QWebEngineFrame::baseUrl_data() -{ - QTest::addColumn<QString>("html"); - QTest::addColumn<QUrl>("loadUrl"); - QTest::addColumn<QUrl>("url"); - QTest::addColumn<QUrl>("baseUrl"); - - QTest::newRow("null") << QString() << QUrl() - << QUrl("about:blank") << QUrl("about:blank"); - - QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") - << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); - - QString html = "<html>" - "<head>" - "<base href=\"http://foobaz.bar/\" />" - "</head>" - "</html>"; - QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") - << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); -} - -void tst_QWebEngineFrame::baseUrl() -{ - QFETCH(QString, html); - QFETCH(QUrl, loadUrl); - QFETCH(QUrl, url); - QFETCH(QUrl, baseUrl); - - QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); - m_page->setHtml(html, loadUrl); - QTRY_COMPARE(loadSpy.count(), 1); - QCOMPARE(m_page->url(), url); - QEXPECT_FAIL("null", "Slight change: We now translate QUrl() to about:blank for the virtual url, but not for the baseUrl", Continue); - QCOMPARE(baseUrlSync(m_page), baseUrl); -} - -void tst_QWebEngineFrame::hasSetFocus() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - QString html("<html><body><p>top</p>" \ - "<iframe width='80%' height='30%'/>" \ - "</body></html>"); - - QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); - m_page->setHtml(html); - - waitForSignal(m_page, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(loadSpy.size(), 1); - - QList<QWebEngineFrame*> children = m_page->childFrames(); - QWebEngineFrame* frame = children.at(0); - QString innerHtml("<html><body><p>another iframe</p>" \ - "<iframe width='80%' height='30%'/>" \ - "</body></html>"); - frame->setHtml(innerHtml); - - waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(loadSpy.size(), 2); - - m_page->setFocus(); - QTRY_VERIFY(m_page->hasFocus()); - - for (int i = 0; i < children.size(); ++i) { - children.at(i)->setFocus(); - QTRY_VERIFY(children.at(i)->hasFocus()); - QVERIFY(!m_page->hasFocus()); - } - - m_page->setFocus(); - QTRY_VERIFY(m_page->hasFocus()); -#endif -} - -void tst_QWebEngineFrame::renderGeometry() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - QString html("<html>" \ - "<head><style>" \ - "body, iframe { margin: 0px; border: none; }" \ - "</style></head>" \ - "<body><iframe width='100px' height='100px'/></body>" \ - "</html>"); - - QWebEnginePage page; - page.setHtml(html); - - QList<QWebEngineFrame*> frames = page.childFrames(); - QWebEngineFrame *frame = frames.at(0); - QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>"); - - // By default, only security origins of local files can load local resources. - // So we should specify baseUrl to be a local file in order to get a proper origin. - frame->setHtml(innerHtml, QUrl("file:///path/to/file")); - waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); - - QPicture picture; - - QSize size = page.contentsSize(); - page.setViewportSize(size); - - // render contents layer only (the iframe is smaller than the image, so it will have scrollbars) - QPainter painter1(&picture); - frame->render(&painter1, QWebEngineFrame::ContentsLayer); - painter1.end(); - - QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width()); - QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height()); - - // render everything, should be the size of the iframe - QPainter painter2(&picture); - frame->render(&painter2, QWebEngineFrame::AllLayers); - painter2.end(); - - QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px - QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px -#endif -} - - -class DummyPaintEngine: public QPaintEngine { -public: - - DummyPaintEngine() - : QPaintEngine(QPaintEngine::AllFeatures) - , renderHints(0) - { - } - - bool begin(QPaintDevice*) - { - setActive(true); - return true; - } - - bool end() - { - setActive(false); - return false; - } - - void updateState(const QPaintEngineState& state) - { - renderHints = state.renderHints(); - } - - void drawPath(const QPainterPath&) { } - void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } - - QPaintEngine::Type type() const - { - return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); - } - - QPainter::RenderHints renderHints; -}; - -class DummyPaintDevice: public QPaintDevice { -public: - DummyPaintDevice() - : QPaintDevice() - , m_engine(new DummyPaintEngine) - { - } - - ~DummyPaintDevice() - { - delete m_engine; - } - - QPaintEngine* paintEngine() const - { - return m_engine; - } - - QPainter::RenderHints renderHints() const - { - return m_engine->renderHints; - } - -protected: - int metric(PaintDeviceMetric metric) const; - -private: - DummyPaintEngine* m_engine; - friend class DummyPaintEngine; -}; - - -int DummyPaintDevice::metric(PaintDeviceMetric metric) const -{ - switch (metric) { - case PdmWidth: - return 400; - break; - - case PdmHeight: - return 200; - break; - - case PdmNumColors: - return INT_MAX; - break; - - case PdmDepth: - return 32; - break; - - default: - break; - } - return 0; -} - -void tst_QWebEngineFrame::renderHints() -{ -#if !defined(QWEBENGINEPAGE_RENDER) - QSKIP("QWEBENGINEPAGE_RENDER"); -#else - QString html("<html><body><p>Hello, world!</p></body></html>"); - - QWebEnginePage page; - page.setHtml(html); - page.setViewportSize(page.contentsSize()); - - // We will call frame->render and trap the paint engine state changes - // to ensure that GraphicsContext does not clobber the render hints. - DummyPaintDevice buffer; - QPainter painter(&buffer); - - painter.setRenderHint(QPainter::TextAntialiasing, false); - page.render(&painter); - QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); - QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::TextAntialiasing, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::HighQualityAntialiasing, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); - QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); -#endif -} - -void tst_QWebEngineFrame::scrollPosition() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - // enlarged image in a small viewport, to provoke the scrollbars to appear - QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); - - QWebEnginePage page; - page.setViewportSize(QSize(200, 200)); - - page.setHtml(html); - page.setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); - page.setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); - - // try to set the scroll offset programmatically - page.setScrollPosition(QPoint(23, 29)); - QCOMPARE(page.scrollPosition().x(), 23); - QCOMPARE(page.scrollPosition().y(), 29); - - int x = page.evaluateJavaScript("window.scrollX").toInt(); - int y = page.evaluateJavaScript("window.scrollY").toInt(); - QCOMPARE(x, 23); - QCOMPARE(y, 29); -#endif -} - -void tst_QWebEngineFrame::scrollToAnchor() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEnginePage page; - page.setViewportSize(QSize(480, 800)); - - QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>" - "<p><a id=\"foo\">This</a> is an anchor</p>" - "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>" - "</body></html>"); - page.setHtml(html); - page.setScrollPosition(QPoint(0, 0)); - QCOMPARE(page.scrollPosition().x(), 0); - QCOMPARE(page.scrollPosition().y(), 0); - - QWebEngineElement fooAnchor = page.findFirstElement("a[id=foo]"); - - page.scrollToAnchor("foo"); - QCOMPARE(page.scrollPosition().y(), fooAnchor.geometry().top()); - - page.scrollToAnchor("bar"); - page.scrollToAnchor("foo"); - QCOMPARE(page.scrollPosition().y(), fooAnchor.geometry().top()); - - page.scrollToAnchor("top"); - QCOMPARE(page.scrollPosition().y(), 0); - - page.scrollToAnchor("bar"); - page.scrollToAnchor("notexist"); - QVERIFY(page.scrollPosition().y() != 0); -#endif -} - - -void tst_QWebEngineFrame::scrollbarsOff() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - QWebEngineView view; - QWebEngineFrame* mainFrame = view.page(); - - mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); - mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); - - QString html("<script>" \ - " function checkScrollbar() {" \ - " if (innerWidth === document.documentElement.offsetWidth)" \ - " document.getElementById('span1').innerText = 'SUCCESS';" \ - " else" \ - " document.getElementById('span1').innerText = 'FAIL';" \ - " }" \ - "</script>" \ - "<body>" \ - " <div style='margin-top:1000px ; margin-left:1000px'>" \ - " <a id='offscreen' href='a'>End</a>" \ - " </div>" \ - "<span id='span1'></span>" \ - "</body>"); - - - QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); - view.setHtml(html); - ::waitForSignal(&view, SIGNAL(loadFinished(bool)), 200); - QCOMPARE(loadSpy.count(), 1); - - mainFrame->evaluateJavaScript("checkScrollbar();"); - QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS")); -#endif -} - -void tst_QWebEngineFrame::horizontalScrollAfterBack() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - QWebEngineView view; - QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); - - view.page()->settings()->setMaximumPagesInCache(2); - view.page()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); - view.page()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); - - view.load(QUrl("qrc:/testiframe2.html")); - view.resize(200, 200); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); - - view.load(QUrl("qrc:/testiframe.html")); - QTRY_COMPARE(loadSpy.count(), 2); - - view.page()->triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(loadSpy.count(), 3); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); -#endif -} - -void tst_QWebEngineFrame::evaluateWillCauseRepaint() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - QWebEngineView view; - QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" - "junk</div>bottom</body></html>"); - view.setHtml(html); - view.show(); - - QTest::qWaitForWindowExposed(&view); - view.page()->evaluateJavaScript( - "document.getElementById('junk').style.display = 'none';"); - - ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); -#endif -} - -void tst_QWebEngineFrame::setContent_data() -{ - QTest::addColumn<QString>("mimeType"); - QTest::addColumn<QByteArray>("testContents"); - QTest::addColumn<QString>("expected"); - - QString str = QString::fromUtf8("ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει"); - QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str; - - QTextCodec *utf16 = QTextCodec::codecForName("UTF-16"); - if (utf16) - QTest::newRow("UTF-16 plain text") << "text/plain; charset=utf-16" << utf16->fromUnicode(str) << str; - - str = QString::fromUtf8("Une chaîne de caractères à sa façon."); - QTest::newRow("latin-1 plain text") << "text/plain; charset=iso-8859-1" << str.toLatin1() << str; - - -} - -void tst_QWebEngineFrame::setContent() -{ - QFETCH(QString, mimeType); - QFETCH(QByteArray, testContents); - QFETCH(QString, expected); - QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); - m_view->setContent(testContents, mimeType); - QVERIFY(loadSpy.wait()); - QCOMPARE(toPlainTextSync(m_view->page()), expected); -} - -class CacheNetworkAccessManager : public QNetworkAccessManager { -public: - CacheNetworkAccessManager(QObject* parent = 0) - : QNetworkAccessManager(parent) - , m_lastCacheLoad(QNetworkRequest::PreferNetwork) - { - } - - virtual QNetworkReply* createRequest(Operation, const QNetworkRequest& request, QIODevice*) - { - QVariant cacheLoad = request.attribute(QNetworkRequest::CacheLoadControlAttribute); - if (cacheLoad.isValid()) - m_lastCacheLoad = static_cast<QNetworkRequest::CacheLoadControl>(cacheLoad.toUInt()); - else - m_lastCacheLoad = QNetworkRequest::PreferNetwork; // default value - return new FakeReply(request, this); - } - - QNetworkRequest::CacheLoadControl lastCacheLoad() const - { - return m_lastCacheLoad; - } - -private: - QNetworkRequest::CacheLoadControl m_lastCacheLoad; -}; - -void tst_QWebEngineFrame::setCacheLoadControlAttribute() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QWebEnginePage page; - CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); - page.setNetworkAccessManager(manager); - - QNetworkRequest request(QUrl("http://abcdef.abcdef/")); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); -#endif -} - -void tst_QWebEngineFrame::setUrlWithPendingLoads() -{ - QWebEnginePage page; - page.setHtml("<img src='dummy:'/>"); - page.setUrl(QUrl("about:blank")); -} - -void tst_QWebEngineFrame::setUrlToEmpty() -{ - int expectedLoadFinishedCount = 0; - const QUrl aboutBlank("about:blank"); - const QUrl url("qrc:/test2.html"); - - QWebEnginePage page; - QCOMPARE(page.url(), QUrl()); - QCOMPARE(page.requestedUrl(), QUrl()); - QCOMPARE(baseUrlSync(&page), QUrl()); - - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - - // Set existing url - page.setUrl(url); - expectedLoadFinishedCount++; - ::waitForSignal(&page, SIGNAL(loadFinished(bool))); - - QCOMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(page.url(), url); - QCOMPARE(page.requestedUrl(), url); - QCOMPARE(baseUrlSync(&page), url); - - // Set empty url - page.setUrl(QUrl()); - expectedLoadFinishedCount++; - - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(page.url(), aboutBlank); - QCOMPARE(page.requestedUrl(), QUrl()); - QCOMPARE(baseUrlSync(&page), aboutBlank); - - // Set existing url - page.setUrl(url); - expectedLoadFinishedCount++; - - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(page.url(), url); - QCOMPARE(page.requestedUrl(), url); - QCOMPARE(baseUrlSync(&page), url); - - // Load empty url - page.load(QUrl()); - expectedLoadFinishedCount++; - - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(page.url(), aboutBlank); - QCOMPARE(page.requestedUrl(), QUrl()); - QCOMPARE(baseUrlSync(&page), aboutBlank); -} - -void tst_QWebEngineFrame::setUrlToInvalid() -{ - QEXPECT_FAIL("", "Unsupported: QtWebEngine doesn't adjust invalid URLs.", Abort); - QVERIFY(false); - - QWebEnginePage page; - - const QUrl invalidUrl("http:/example.com"); - QVERIFY(!invalidUrl.isEmpty()); - QVERIFY(invalidUrl != QUrl()); - - // QWebEngineFrame will do its best to accept the URL, possible converting it to a valid equivalent URL. - const QUrl validUrl("http://example.com/"); - page.setUrl(invalidUrl); - QCOMPARE(page.url(), validUrl); - QCOMPARE(page.requestedUrl(), validUrl); - QCOMPARE(baseUrlSync(&page), validUrl); - - // QUrls equivalent to QUrl() will be treated as such. - const QUrl aboutBlank("about:blank"); - const QUrl anotherInvalidUrl("1http://bugs.webkit.org"); - QVERIFY(!anotherInvalidUrl.isEmpty()); // and they are not necessarily empty. - QVERIFY(!anotherInvalidUrl.isValid()); - QCOMPARE(anotherInvalidUrl.toEncoded(), QUrl().toEncoded()); - - page.setUrl(anotherInvalidUrl); - QCOMPARE(page.url(), aboutBlank); - QCOMPARE(page.requestedUrl().toEncoded(), anotherInvalidUrl.toEncoded()); - QCOMPARE(baseUrlSync(&page), aboutBlank); -} - -static QStringList collectHistoryUrls(QWebEngineHistory *history) -{ - QStringList urls; - foreach (const QWebEngineHistoryItem &i, history->items()) - urls << i.url().toString(); - return urls; -} - -void tst_QWebEngineFrame::setUrlHistory() -{ - const QUrl aboutBlank("about:blank"); - QUrl url; - int expectedLoadFinishedCount = 0; - QSignalSpy spy(m_page, SIGNAL(loadFinished(bool))); - - QCOMPARE(m_page->history()->count(), 0); - - m_page->setUrl(QUrl()); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(m_page->url(), aboutBlank); - QCOMPARE(m_page->requestedUrl(), QUrl()); - // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString()); - - url = QUrl("http://non.existent/"); - m_page->setUrl(url); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - // When error page is disabled in case of LoadFail the entry of the unavailable page is not stored. - // We expect the url of the previously loaded page here. - QCOMPARE(m_page->url(), aboutBlank); - QCOMPARE(m_page->requestedUrl(), QUrl()); - // Since the entry of the unavailable page is not stored it will not available in the history. - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString()); - - url = QUrl("qrc:/test1.html"); - m_page->setUrl(url); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->requestedUrl(), url); - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString() << QStringLiteral("qrc:/test1.html")); - - m_page->setUrl(QUrl()); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(m_page->url(), aboutBlank); - QCOMPARE(m_page->requestedUrl(), QUrl()); - // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() - << aboutBlank.toString() - << QStringLiteral("qrc:/test1.html") - << aboutBlank.toString()); - - url = QUrl("qrc:/test1.html"); - m_page->setUrl(url); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->requestedUrl(), url); - // The history count DOES change since the about:blank is in the list. - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() - << aboutBlank.toString() - << QStringLiteral("qrc:/test1.html") - << aboutBlank.toString() - << QStringLiteral("qrc:/test1.html")); - - url = QUrl("qrc:/test2.html"); - m_page->setUrl(url); - expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->requestedUrl(), url); - QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() - << aboutBlank.toString() - << QStringLiteral("qrc:/test1.html") - << aboutBlank.toString() - << QStringLiteral("qrc:/test1.html") - << QStringLiteral("qrc:/test2.html")); -} - -void tst_QWebEngineFrame::setUrlUsingStateObject() -{ - const QUrl aboutBlank("about:blank"); - QUrl url; - QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl))); - int expectedUrlChangeCount = 0; - - QCOMPARE(m_page->history()->count(), 0); - - url = QUrl("qrc:/test1.html"); - m_page->setUrl(url); - waitForSignal(m_page, SIGNAL(loadFinished(bool))); - expectedUrlChangeCount++; - QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->history()->count(), 1); - - evaluateJavaScriptSync(m_page, "window.history.pushState(null, 'push', 'navigate/to/here')"); - expectedUrlChangeCount++; - QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); - QCOMPARE(m_page->url(), QUrl("qrc:/navigate/to/here")); - QCOMPARE(m_page->history()->count(), 2); - QVERIFY(m_page->history()->canGoBack()); - - evaluateJavaScriptSync(m_page, "window.history.replaceState(null, 'replace', 'another/location')"); - expectedUrlChangeCount++; - QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); - QCOMPARE(m_page->url(), QUrl("qrc:/navigate/to/another/location")); - QCOMPARE(m_page->history()->count(), 2); - QVERIFY(!m_page->history()->canGoForward()); - QVERIFY(m_page->history()->canGoBack()); - - evaluateJavaScriptSync(m_page, "window.history.back()"); - QTest::qWait(100); - expectedUrlChangeCount++; - QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); - QCOMPARE(m_page->url(), QUrl("qrc:/test1.html")); - QVERIFY(m_page->history()->canGoForward()); - QVERIFY(!m_page->history()->canGoBack()); -} - -void tst_QWebEngineFrame::setUrlSameUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - const QUrl url1("qrc:/test1.html"); - const QUrl url2("qrc:/test2.html"); - - QWebEnginePage page; - FakeNetworkManager* networkManager = new FakeNetworkManager(&page); - page.setNetworkAccessManager(networkManager); - - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - - page.setUrl(url1); - waitForSignal(&page, SIGNAL(loadFinished(bool))); - QVERIFY(page.url() != url1); // Nota bene: our QNAM redirects url1 to url2 - QCOMPARE(page.url(), url2); - QCOMPARE(spy.count(), 1); - - page.setUrl(url1); - waitForSignal(&page, SIGNAL(loadFinished(bool))); - QVERIFY(page.url() != url1); - QCOMPARE(page.url(), url2); - QCOMPARE(spy.count(), 2); - - // Now a case without redirect. The existing behavior we have for setUrl() - // is more like a "clear(); load()", so the page will be loaded again, even - // if urlToBeLoaded == url(). This test should be changed if we want to - // make setUrl() early return in this case. - page.setUrl(url2); - waitForSignal(&page, SIGNAL(loadFinished(bool))); - QCOMPARE(page.url(), url2); - QCOMPARE(spy.count(), 3); - - page.setUrl(url1); - waitForSignal(&page, SIGNAL(loadFinished(bool))); - QCOMPARE(page.url(), url2); - QCOMPARE(spy.count(), 4); -#endif -} - -static inline QUrl extractBaseUrl(const QUrl& url) -{ - return url.resolved(QUrl()); -} - -void tst_QWebEngineFrame::setUrlThenLoads_data() -{ - QTest::addColumn<QUrl>("url"); - QTest::addColumn<QUrl>("baseUrl"); - - QTest::newRow("resource file") << QUrl("qrc:/test1.html") << extractBaseUrl(QUrl("qrc:/test1.html")); - QTest::newRow("base specified in HTML") << QUrl("data:text/html,<head><base href=\"http://different.base/\"></head>") << QUrl("http://different.base/"); -} - -void tst_QWebEngineFrame::setUrlThenLoads() -{ - QFETCH(QUrl, url); - QFETCH(QUrl, baseUrl); - QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl))); - QSignalSpy startedSpy(m_page, SIGNAL(loadStarted())); - QSignalSpy finishedSpy(m_page, SIGNAL(loadFinished(bool))); - - m_page->setUrl(url); - QTRY_COMPARE(startedSpy.count(), 1); - QTRY_COMPARE(urlChangedSpy.count(), 1); - QTRY_COMPARE(finishedSpy.count(), 1); - QVERIFY(finishedSpy.at(0).first().toBool()); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->requestedUrl(), url); - QCOMPARE(baseUrlSync(m_page), baseUrl); - - const QUrl urlToLoad1("qrc:/test2.html"); - const QUrl urlToLoad2("qrc:/test1.html"); - - // Just after first load. URL didn't changed yet. - m_page->load(urlToLoad1); - QCOMPARE(m_page->url(), url); - QCOMPARE(m_page->requestedUrl(), urlToLoad1); - // baseUrlSync spins an event loop and this sometimes return the next result. - // QCOMPARE(baseUrlSync(m_page), baseUrl); - QTRY_COMPARE(startedSpy.count(), 2); - - // After first URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 2); - QTRY_COMPARE(finishedSpy.count(), 2); - QVERIFY(finishedSpy.at(1).first().toBool()); - QCOMPARE(m_page->url(), urlToLoad1); - QCOMPARE(m_page->requestedUrl(), urlToLoad1); - QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); - - // Just after second load. URL didn't changed yet. - m_page->load(urlToLoad2); - QCOMPARE(m_page->url(), urlToLoad1); - QCOMPARE(m_page->requestedUrl(), urlToLoad2); - QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); - QTRY_COMPARE(startedSpy.count(), 3); - - // After second URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 3); - QTRY_COMPARE(finishedSpy.count(), 3); - QVERIFY(finishedSpy.at(2).first().toBool()); - QCOMPARE(m_page->url(), urlToLoad2); - QCOMPARE(m_page->requestedUrl(), urlToLoad2); - QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad2)); -} - -void tst_QWebEngineFrame::loadFinishedAfterNotFoundError() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QWebEnginePage page; - - QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - FakeNetworkManager* networkManager = new FakeNetworkManager(&page); - page.setNetworkAccessManager(networkManager); - - page.setUrl(FakeReply::urlFor404ErrorWithoutContents); - QTRY_COMPARE(spy.count(), 1); - const bool wasLoadOk = spy.at(0).at(0).toBool(); - QVERIFY(!wasLoadOk); -#endif -} - -class URLSetter : public QObject { - Q_OBJECT - -public: - enum Signal { - LoadStarted, - LoadFinished, - ProvisionalLoad - }; - - enum Type { - UseLoad, - UseSetUrl - }; - - URLSetter(QWebEnginePage*, Signal, Type, const QUrl&); - -public Q_SLOTS: - void execute(); - -Q_SIGNALS: - void finished(); - -private: - QWebEnginePage* m_page; - QUrl m_url; - Type m_type; -}; - -Q_DECLARE_METATYPE(URLSetter::Signal) -Q_DECLARE_METATYPE(URLSetter::Type) - -URLSetter::URLSetter(QWebEnginePage* page, Signal signal, URLSetter::Type type, const QUrl& url) - : m_page(page), m_url(url), m_type(type) -{ - if (signal == LoadStarted) - connect(m_page, SIGNAL(loadStarted()), SLOT(execute())); - else if (signal == LoadFinished) - connect(m_page, SIGNAL(loadFinished(bool)), SLOT(execute())); - else - connect(m_page, SIGNAL(provisionalLoad()), SLOT(execute())); -} - -void URLSetter::execute() -{ - // We track only the first emission. - m_page->disconnect(this); - if (m_type == URLSetter::UseLoad) - m_page->load(m_url); - else - m_page->setUrl(m_url); - connect(m_page, SIGNAL(loadFinished(bool)), SIGNAL(finished())); -} - -void tst_QWebEngineFrame::loadInSignalHandlers_data() -{ - QSKIP("FIXME: This crashes in content::WebContentsImpl::NavigateToEntry because of reentrancy. Should we require QueuedConnections or do it ourselves to support this?"); - - QTest::addColumn<URLSetter::Type>("type"); - QTest::addColumn<URLSetter::Signal>("signal"); - QTest::addColumn<QUrl>("url"); - - const QUrl validUrl("qrc:/test2.html"); - const QUrl invalidUrl("qrc:/invalid"); - - QTest::newRow("call load() in loadStarted() after valid url") << URLSetter::UseLoad << URLSetter::LoadStarted << validUrl; - QTest::newRow("call load() in loadStarted() after invalid url") << URLSetter::UseLoad << URLSetter::LoadStarted << invalidUrl; - QTest::newRow("call load() in loadFinished() after valid url") << URLSetter::UseLoad << URLSetter::LoadFinished << validUrl; - QTest::newRow("call load() in loadFinished() after invalid url") << URLSetter::UseLoad << URLSetter::LoadFinished << invalidUrl; - QTest::newRow("call load() in provisionalLoad() after valid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << validUrl; - QTest::newRow("call load() in provisionalLoad() after invalid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << invalidUrl; - - QTest::newRow("call setUrl() in loadStarted() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << validUrl; - QTest::newRow("call setUrl() in loadStarted() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << invalidUrl; - QTest::newRow("call setUrl() in loadFinished() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << validUrl; - QTest::newRow("call setUrl() in loadFinished() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << invalidUrl; - QTest::newRow("call setUrl() in provisionalLoad() after valid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << validUrl; - QTest::newRow("call setUrl() in provisionalLoad() after invalid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << invalidUrl; -} - -void tst_QWebEngineFrame::loadInSignalHandlers() -{ - QFETCH(URLSetter::Type, type); - QFETCH(URLSetter::Signal, signal); - QFETCH(QUrl, url); - - const QUrl urlForSetter("qrc:/test1.html"); - URLSetter setter(m_page, signal, type, urlForSetter); - - m_page->load(url); - waitForSignal(&setter, SIGNAL(finished()), 200); - QCOMPARE(m_page->url(), urlForSetter); -} - -QTEST_MAIN(tst_QWebEngineFrame) -#include "tst_qwebengineframe.moc" diff --git a/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.qrc b/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.qrc deleted file mode 100644 index 2a7d0b9c2..000000000 --- a/tests/auto/widgets/qwebengineframe/tst_qwebengineframe.qrc +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource prefix="/"> -<file alias="image.png">resources/image.png</file> -<file alias="style.css">resources/style.css</file> -<file alias="test1.html">resources/test1.html</file> -<file alias="test2.html">resources/test2.html</file> -<file alias="testiframe.html">resources/testiframe.html</file> -<file alias="testiframe2.html">resources/testiframe2.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro b/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro +++ b/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro b/tests/auto/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro +++ b/tests/auto/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebengineinspector/qwebengineinspector.pro b/tests/auto/widgets/qwebengineinspector/qwebengineinspector.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebengineinspector/qwebengineinspector.pro +++ b/tests/auto/widgets/qwebengineinspector/qwebengineinspector.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro index e56bbe8f7..70786e70f 100644 --- a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro +++ b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro @@ -1,3 +1,2 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc QT *= core-private gui-private diff --git a/tests/auto/widgets/qwebenginepage/resources/fullscreen.html b/tests/auto/widgets/qwebenginepage/resources/fullscreen.html new file mode 100644 index 000000000..84771ca85 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/fullscreen.html @@ -0,0 +1,10 @@ +<html> +<body onkeypress='onKeyPress()'> +<a>This is test content</a> +<script> +function onKeyPress() { + document.documentElement.webkitRequestFullScreen(); +} +</script> +</body> +</html> diff --git a/tests/auto/widgets/qwebengineframe/resources/image.png b/tests/auto/widgets/qwebenginepage/resources/image.png Binary files differindex 8d703640c..8d703640c 100644 --- a/tests/auto/widgets/qwebengineframe/resources/image.png +++ b/tests/auto/widgets/qwebenginepage/resources/image.png diff --git a/tests/auto/widgets/qwebengineframe/resources/style.css b/tests/auto/widgets/qwebenginepage/resources/style.css index c05b747f1..c05b747f1 100644 --- a/tests/auto/widgets/qwebengineframe/resources/style.css +++ b/tests/auto/widgets/qwebenginepage/resources/style.css diff --git a/tests/auto/widgets/qwebengineframe/resources/test1.html b/tests/auto/widgets/qwebenginepage/resources/test1.html index b323f966e..b323f966e 100644 --- a/tests/auto/widgets/qwebengineframe/resources/test1.html +++ b/tests/auto/widgets/qwebenginepage/resources/test1.html diff --git a/tests/auto/widgets/qwebengineframe/resources/test2.html b/tests/auto/widgets/qwebenginepage/resources/test2.html index 63ac1f6ec..63ac1f6ec 100644 --- a/tests/auto/widgets/qwebengineframe/resources/test2.html +++ b/tests/auto/widgets/qwebenginepage/resources/test2.html diff --git a/tests/auto/widgets/qwebengineframe/resources/testiframe.html b/tests/auto/widgets/qwebenginepage/resources/testiframe.html index 4b0e30ca5..4b0e30ca5 100644 --- a/tests/auto/widgets/qwebengineframe/resources/testiframe.html +++ b/tests/auto/widgets/qwebenginepage/resources/testiframe.html diff --git a/tests/auto/widgets/qwebengineframe/resources/testiframe2.html b/tests/auto/widgets/qwebenginepage/resources/testiframe2.html index 8957a5d8a..8957a5d8a 100644 --- a/tests/auto/widgets/qwebengineframe/resources/testiframe2.html +++ b/tests/auto/widgets/qwebenginepage/resources/testiframe2.html diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 9562871b3..ffd8ccba7 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -27,6 +27,7 @@ #include <QMainWindow> #include <QMenu> #include <QMimeDatabase> +#include <QPaintEngine> #include <QPushButton> #include <QStateMachine> #include <QStyle> @@ -37,8 +38,10 @@ #include <qnetworkreply.h> #include <qnetworkrequest.h> #include <qpa/qplatforminputcontext.h> +#include <qwebenginefullscreenrequest.h> #include <qwebenginehistory.h> #include <qwebenginepage.h> +#include <qwebengineprofile.h> #include <qwebenginesettings.h> #include <qwebengineview.h> #include <qimagewriter.h> @@ -95,6 +98,8 @@ public: tst_QWebEnginePage(); virtual ~tst_QWebEnginePage(); + bool eventFilter(QObject *watched, QEvent *event); + public Q_SLOTS: void init(); void cleanup(); @@ -108,6 +113,7 @@ private Q_SLOTS: void contextMenuPopulatedOnce(); void acceptNavigationRequest(); void acceptNavigationRequestNavigationType(); + void geolocationRequestJS_data(); void geolocationRequestJS(); void loadFinished(); void actionStates(); @@ -132,7 +138,6 @@ private Q_SLOTS: void textSelection(); void textEditing(); void backActionUpdate(); - void frameAt(); void protectBindingsRuntimeObjectsFromCollector(); void localURLSchemes(); void testOptionalJSObjects(); @@ -145,10 +150,7 @@ private Q_SLOTS: void inputMethodsTextFormat(); void defaultTextEncoding(); void errorPageExtension(); - void errorPageExtensionInIFrames(); - void errorPageExtensionInFrameset(); void errorPageExtensionLoadFinished(); - void userAgentApplicationName(); void userAgentNewlineStripping(); void undoActionHaveCustomText(); void renderWidgetHostViewNotShowTopLevel(); @@ -166,9 +168,6 @@ private Q_SLOTS: void unacceleratedWebGLScreenshotWithoutView(); #endif - void originatingObjectInNetworkRequests(); - void networkReplyParentDidntChange(); - void destroyQNAMBeforeAbortDoesntCrash(); void testJSPrompt(); void testStopScheduledPageRefresh(); void findText(); @@ -192,10 +191,57 @@ private Q_SLOTS: #endif void runJavaScript(); + void fullScreenRequested(); + + + // Tests from tst_QWebEngineFrame + void horizontalScrollAfterBack(); + void symmetricUrl(); + void progressSignal(); + void urlChange(); + void requestedUrlAfterSetAndLoadFailures(); + void javaScriptWindowObjectCleared_data(); + void javaScriptWindowObjectCleared(); + void javaScriptWindowObjectClearedOnEvaluate(); + void asyncAndDelete(); + void earlyToHtml(); + void setHtml(); + void setHtmlWithImageResource(); + void setHtmlWithStylesheetResource(); + void setHtmlWithBaseURL(); + void setHtmlWithJSAlert(); + void metaData(); +#if !defined(QT_NO_COMBOBOX) + void popupFocus(); +#endif + void inputFieldFocus(); + void hitTestContent(); + void baseUrl_data(); + void baseUrl(); + void renderHints(); + void scrollPosition(); + void scrollToAnchor(); + void scrollbarsOff(); + void evaluateWillCauseRepaint(); + void setContent_data(); + void setContent(); + void setCacheLoadControlAttribute(); + void setUrlWithPendingLoads(); + void setUrlToEmpty(); + void setUrlToInvalid(); + void setUrlHistory(); + void setUrlUsingStateObject(); + void setUrlThenLoads_data(); + void setUrlThenLoads(); + void loadFinishedAfterNotFoundError(); + void loadInSignalHandlers_data(); + void loadInSignalHandlers(); private: QWebEngineView* m_view; QWebEnginePage* m_page; + QWebEngineView* m_inputFieldsTestView; + int m_inputFieldTestPaintCount; QString tmpDirPath() const { static QString tmpd = QDir::tempPath() + "/tst_qwebenginepage-" @@ -212,10 +258,21 @@ tst_QWebEnginePage::~tst_QWebEnginePage() { } +bool tst_QWebEnginePage::eventFilter(QObject* watched, QEvent* event) +{ + // used on the inputFieldFocus test + if (watched == m_inputFieldsTestView) { + if (event->type() == QEvent::Paint) + m_inputFieldTestPaintCount++; + } + return QObject::eventFilter(watched, event); +} + void tst_QWebEnginePage::init() { m_view = new QWebEngineView(); m_page = m_view->page(); + m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); } void tst_QWebEnginePage::cleanup() @@ -278,7 +335,6 @@ void tst_QWebEnginePage::acceptNavigationRequest() m_view->setPage(0); } -#if defined(QWEBENGINEPAGE_SETFEATUREPERMISSION) class JSTestPage : public QWebEnginePage { Q_OBJECT @@ -291,12 +347,12 @@ public: return true; } public Q_SLOTS: - void requestPermission(QWebEngineFrame* frame, QWebEnginePage::Feature feature) + void requestPermission(const QUrl &origin, QWebEnginePage::Feature feature) { if (m_allowGeolocation) - setFeaturePermission(frame, feature, PermissionGrantedByUser); + setFeaturePermission(origin, feature, PermissionGrantedByUser); else - setFeaturePermission(frame, feature, PermissionDeniedByUser); + setFeaturePermission(origin, feature, PermissionDeniedByUser); } public: @@ -308,7 +364,6 @@ public: private: bool m_allowGeolocation; }; -#endif // [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT // https://bugs.webkit.org/show_bug.cgi?id=79040 @@ -323,40 +378,38 @@ void tst_QWebEnginePage::infiniteLoopJS() } */ -void tst_QWebEnginePage::geolocationRequestJS() +void tst_QWebEnginePage::geolocationRequestJS_data() { -#if !defined(QWEBENGINEPAGE_SETFEATUREPERMISSION) - QSKIP("QWEBENGINEPAGE_SETFEATUREPERMISSION"); -#else - JSTestPage* newPage = new JSTestPage(m_view); + QTest::addColumn<bool>("allowed"); + QTest::addColumn<int>("errorCode"); + QTest::newRow("allowed") << true << 0; + QTest::newRow("not allowed") << false << 1; +} +void tst_QWebEnginePage::geolocationRequestJS() +{ + QFETCH(bool, allowed); + QFETCH(int, errorCode); + QWebEngineView *view = new QWebEngineView; + JSTestPage *newPage = new JSTestPage(view); + newPage->setView(view); + newPage->setGeolocationPermission(allowed); + + connect(newPage, SIGNAL(featurePermissionRequested(const QUrl&, QWebEnginePage::Feature)), + newPage, SLOT(requestPermission(const QUrl&, QWebEnginePage::Feature))); + + QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool))); + newPage->setHtml(QString("<html><body>test</body></html>"), QUrl()); + QTRY_COMPARE(spyLoadFinished.count(), 1); if (evaluateJavaScriptSync(newPage, QLatin1String("!navigator.geolocation")).toBool()) { - delete newPage; + delete view; W_QSKIP("Geolocation is not supported.", SkipSingle); } - connect(newPage, SIGNAL(featurePermissionRequested(QWebEngineFrame*, QWebEnginePage::Feature)), - newPage, SLOT(requestPermission(QWebEngineFrame*, QWebEnginePage::Feature))); - - newPage->setGeolocationPermission(false); - m_view->setPage(newPage); - m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); - evaluateJavaScriptSync(m_view->page(), "var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); - QTest::qWait(2000); - QVariant empty = evaluateJavaScriptSync(m_view->page(), "errorCode"); - - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=102235", Continue); - QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0); + evaluateJavaScriptSync(newPage, "var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); - newPage->setGeolocationPermission(true); - evaluateJavaScriptSync(m_view->page(), "errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);"); - empty = evaluateJavaScriptSync(m_view->page(), "errorCode"); - - //http://dev.w3.org/geo/api/spec-source.html#position - //PositionError: const unsigned short PERMISSION_DENIED = 1; - QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1); - delete newPage; -#endif + QTRY_COMPARE(evaluateJavaScriptSync(newPage, "errorCode").toInt(), errorCode); + delete view; } void tst_QWebEnginePage::loadFinished() @@ -1176,6 +1229,10 @@ public: int isSelectionCollapsed() { return evaluateJavaScriptSync(this, "window.getSelection().getRangeAt(0).collapsed").toBool(); } + bool hasSelection() + { + return !selectedText().isEmpty(); + } }; void tst_QWebEnginePage::cursorMovements() @@ -1380,17 +1437,19 @@ void tst_QWebEnginePage::cursorMovements() void tst_QWebEnginePage::textSelection() { -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - CursorTrackedPage* page = new CursorTrackedPage; + QWebEngineView *view = new QWebEngineView; + CursorTrackedPage *page = new CursorTrackedPage; QString content("<html><body><p id=one>The quick brown fox</p>" \ "<p id=two>jumps over the lazy dog</p>" \ "<p>May the source<br/>be with you!</p></body></html>"); + page->setView(view); + QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool))); page->setHtml(content); + QTRY_COMPARE(loadSpy.count(), 1); // these actions must exist QVERIFY(page->action(QWebEnginePage::SelectAll) != 0); +#if defined(QWEBENGINEPAGE_SELECTACTIONS) QVERIFY(page->action(QWebEnginePage::SelectNextChar) != 0); QVERIFY(page->action(QWebEnginePage::SelectPreviousChar) != 0); QVERIFY(page->action(QWebEnginePage::SelectNextWord) != 0); @@ -1418,6 +1477,7 @@ void tst_QWebEnginePage::textSelection() QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), false); QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), false); QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false); +#endif // ..but SelectAll is awalys enabled QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true); @@ -1432,13 +1492,15 @@ void tst_QWebEnginePage::textSelection() "getSelection().addRange(range);"; evaluateJavaScriptSync(page, selectScript); QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); +#if defined(QWEBENGINEPAGE_SELECTEDHTML) QRegExp regExp(" style=\".*\""); regExp.setMinimal(true); QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>")); - +#endif // Make sure hasSelection returns true, since there is selected text now... QCOMPARE(page->hasSelection(), true); +#if defined(QWEBENGINEPAGE_SELECTACTIONS) // here the actions are enabled after a selection has been created QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); @@ -1474,9 +1536,10 @@ void tst_QWebEnginePage::textSelection() QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); +#endif delete page; -#endif + delete view; } void tst_QWebEnginePage::textEditing() @@ -1621,39 +1684,6 @@ void tst_QWebEnginePage::backActionUpdate() QVERIFY(action->isEnabled()); } -#if defined(QWEBENGINEFRAME) -void frameAtHelper(QWebEnginePage* webPage, QWebEngineFrame* webFrame, QPoint framePosition) -{ - if (!webFrame) - return; - - framePosition += QPoint(webFrame->pos()); - QList<QWebEngineFrame*> children = webFrame->childFrames(); - for (int i = 0; i < children.size(); ++i) { - if (children.at(i)->childFrames().size() > 0) - frameAtHelper(webPage, children.at(i), framePosition); - - QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size()); - QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft())); - } -} -#endif - -void tst_QWebEnginePage::frameAt() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - QWebEngineView webView; - QWebEnginePage* webPage = webView.page(); - QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); - QUrl url = QUrl("qrc:///resources/iframe.html"); - webPage->load(url); - QTRY_COMPARE(loadSpy.count(), 1); - frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos()); -#endif -} - void tst_QWebEnginePage::inputMethods_data() { QTest::addColumn<QString>("viewType"); @@ -2726,47 +2756,6 @@ void tst_QWebEnginePage::errorPageExtension() #endif } -void tst_QWebEnginePage::errorPageExtensionInIFrames() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - ErrorPage page; - m_view->setPage(&page); - - m_view->page()->load(QUrl( - "data:text/html," - "<h1>h1</h1>" - "<iframe src='data:text/html,<p/>p'></iframe>" - "<iframe src='http://non.existent/url'></iframe>")); - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - QTRY_COMPARE(spyLoadFinished.count(), 1); - - QCOMPARE(page.mainFrame()->childFrames()[1]->toPlainText(), QString("error")); - - m_view->setPage(0); -#endif -} - -void tst_QWebEnginePage::errorPageExtensionInFrameset() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - ErrorPage page; - m_view->setPage(&page); - - m_view->load(QUrl("qrc:///resources/index.html")); - - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QCOMPARE(page.mainFrame()->childFrames().count(), 2); - QCOMPARE(page.mainFrame()->childFrames()[1]->toPlainText(), QString("error")); - - m_view->setPage(0); -#endif -} - void tst_QWebEnginePage::errorPageExtensionLoadFinished() { #if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) @@ -2800,50 +2789,16 @@ void tst_QWebEnginePage::errorPageExtensionLoadFinished() #endif } -class FriendlyWebPage : public QWebEnginePage -{ -public: - friend class tst_QWebEnginePage; -}; - -void tst_QWebEnginePage::userAgentApplicationName() +void tst_QWebEnginePage::userAgentNewlineStripping() { -#if !defined(QWEBENGINEPAGE_USERAGENTFORURL) - QSKIP("QWEBENGINEPAGE_USERAGENTFORURL"); -#else - const QString oldApplicationName = QCoreApplication::applicationName(); - FriendlyWebPage page; + QWebEngineProfile profile; + QWebEnginePage page(&profile); - const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236"); - QCoreApplication::setApplicationName(applicationNameMarker); - QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker)); + profile.setHttpUserAgent(QStringLiteral("My User Agent\nX-New-Http-Header: Oh Noes!")); + // The user agent will be updated after a page load. + page.load(QUrl("about:blank")); - QCoreApplication::setApplicationName(oldApplicationName); -#endif -} - -class CustomUserAgentWebPage : public QWebEnginePage -{ -public: - static const QLatin1String filteredUserAgent; -protected: - virtual QString userAgentForUrl(const QUrl& url) const - { - Q_UNUSED(url); - return QString("My User Agent\nX-New-Http-Header: Oh Noes!"); - } -}; -const QLatin1String CustomUserAgentWebPage::filteredUserAgent("My User AgentX-New-Http-Header: Oh Noes!"); - -void tst_QWebEnginePage::userAgentNewlineStripping() -{ -#if !defined(QWEBENGINEPAGE_USERAGENTFORURL) - QSKIP("QWEBENGINEPAGE_USERAGENTFORURL"); -#else - CustomUserAgentWebPage page; - page.setHtml("<html><body></body></html>"); - QCOMPARE(evaluateJavaScriptSync(&page, "navigator.userAgent").toString(), CustomUserAgentWebPage::filteredUserAgent); -#endif + QCOMPARE(evaluateJavaScriptSync(&page, "navigator.userAgent").toString(), QStringLiteral("My User Agent X-New-Http-Header: Oh Noes!")); } void tst_QWebEnginePage::crashTests_LazyInitializationOfMainFrame() @@ -2951,65 +2906,6 @@ void tst_QWebEnginePage::unacceleratedWebGLScreenshotWithoutView() } #endif -void tst_QWebEnginePage::originatingObjectInNetworkRequests() -{ -#if !defined(QWEBENGINEFRAME) - QSKIP("QWEBENGINEFRAME"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - networkManager->requests.clear(); - - m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html," - "<head><meta http-equiv='refresh' content='1'></head>foo \">" - "<frame src=\"data:text/html,bar\"></frameset>"), QUrl()); - QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); - - QCOMPARE(networkManager->requests.count(), 2); - - QList<QWebEngineFrame*> childFrames = m_page->mainFrame()->childFrames(); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118660", Continue); - QCOMPARE(childFrames.count(), 2); - - for (int i = 0; i < 2; ++i) - QVERIFY(qobject_cast<QWebEngineFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i)); -#endif -} - -void tst_QWebEnginePage::networkReplyParentDidntChange() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - networkManager->requests.clear(); - - // Trigger a load and check that pending QNetworkReplies haven't been reparented before returning to the event loop. - m_view->load(QUrl("qrc:///resources/content.html")); - - QVERIFY(networkManager->requests.count() > 0); - QVERIFY(networkManager->findChildren<QNetworkReply*>().size() > 0); -#endif -} - -void tst_QWebEnginePage::destroyQNAMBeforeAbortDoesntCrash() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QNetworkAccessManager* networkManager = new QNetworkAccessManager; - m_page->setNetworkAccessManager(networkManager); - - m_view->load(QUrl("qrc:///resources/content.html")); - delete networkManager; - // This simulates what PingLoader does with its QNetworkReply when it times out. - // PingLoader isn't attached to a QWebEnginePage and can be kept alive - // for 60000 seconds (~16.7 hours) to then cancel its ResourceHandle. - m_view->stop(); -#endif -} - /** * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914 * @@ -3109,13 +3005,16 @@ void tst_QWebEnginePage::testStopScheduledPageRefresh() void tst_QWebEnginePage::findText() { - m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); -#if defined(QWEBENGINEPAGE_TRIGGERACTION_SELECTALL) + QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); + m_page->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); m_page->triggerAction(QWebEnginePage::SelectAll); - QVERIFY(!m_page->selectedText().isEmpty()); + QTRY_COMPARE(m_page->hasSelection(), true); +#if defined(QWEBENGINEPAGE_SELECTEDHTML) QVERIFY(!m_page->selectedHtml().isEmpty()); #endif m_page->findText(""); + QEXPECT_FAIL("", "Unsupported: findText only highlights and doesn't update the selection.", Continue); QVERIFY(m_page->selectedText().isEmpty()); #if defined(QWEBENGINEPAGE_SELECTEDHTML) QVERIFY(m_page->selectedHtml().isEmpty()); @@ -3129,6 +3028,7 @@ void tst_QWebEnginePage::findText() QVERIFY(m_page->selectedHtml().contains(subString)); #endif m_page->findText(""); + QEXPECT_FAIL("", "Unsupported: findText only highlights and doesn't update the selection.", Continue); QVERIFY(m_page->selectedText().isEmpty()); #if defined(QWEBENGINEPAGE_SELECTEDHTML) QVERIFY(m_page->selectedHtml().isEmpty()); @@ -3674,41 +3574,115 @@ void tst_QWebEnginePage::cssMediaTypePageSetting() #endif } -class JavaScriptCallback +class JavaScriptCallbackBase +{ +public: + JavaScriptCallbackBase() + { + if (watcher) + QMetaObject::invokeMethod(watcher, "add"); + } + + void operator() (const QVariant &result) + { + check(result); + if (watcher) + QMetaObject::invokeMethod(watcher, "notify"); + } + +protected: + virtual void check(const QVariant &result) = 0; + +private: + friend class JavaScriptCallbackWatcher; + static QPointer<QObject> watcher; +}; + +QPointer<QObject> JavaScriptCallbackBase::watcher = 0; + +class JavaScriptCallback : public JavaScriptCallbackBase { public: JavaScriptCallback() { } JavaScriptCallback(const QVariant& _expected) : expected(_expected) { } - virtual void operator() (const QVariant& result) { + + void check(const QVariant& result) Q_DECL_OVERRIDE + { QVERIFY(result.isValid()); QCOMPARE(result, expected); } + private: QVariant expected; }; -class JavaScriptCallbackNull +class JavaScriptCallbackNull : public JavaScriptCallbackBase { public: - virtual void operator() (const QVariant& result) { + void check(const QVariant& result) Q_DECL_OVERRIDE + { QVERIFY(result.isNull()); // FIXME: Returned null values are currently invalid QVariants. // QVERIFY(result.isValid()); } }; -class JavaScriptCallbackUndefined +class JavaScriptCallbackUndefined : public JavaScriptCallbackBase { public: - virtual void operator() (const QVariant& result) { + void check(const QVariant& result) Q_DECL_OVERRIDE + { QVERIFY(result.isNull()); QVERIFY(!result.isValid()); } }; +class JavaScriptCallbackWatcher : public QObject +{ + Q_OBJECT +public: + JavaScriptCallbackWatcher() + { + Q_ASSERT(!JavaScriptCallbackBase::watcher); + JavaScriptCallbackBase::watcher = this; + } + + Q_INVOKABLE void add() + { + available++; + } + + Q_INVOKABLE void notify() + { + called++; + if (called == available) + emit allCalled(); + } + + bool wait(int maxSeconds = 30) + { + if (called == available) + return true; + + QTestEventLoop loop; + connect(this, SIGNAL(allCalled()), &loop, SLOT(exitLoop())); + loop.enterLoop(maxSeconds); + return !loop.timeout(); + } + +signals: + void allCalled(); + +private: + int available = 0; + int called = 0; +}; + + void tst_QWebEnginePage::runJavaScript() { TestPage page; + JavaScriptCallbackWatcher watcher; JavaScriptCallback callbackBool(QVariant(false)); page.runJavaScript("false", QWebEngineCallback<const QVariant&>(callbackBool)); @@ -3734,10 +3708,1363 @@ void tst_QWebEnginePage::runJavaScript() JavaScriptCallbackNull callbackNull; page.runJavaScript("null", QWebEngineCallback<const QVariant&>(callbackNull)); - JavaScriptCallbackNull callbackUndefined; + JavaScriptCallbackUndefined callbackUndefined; page.runJavaScript("undefined", QWebEngineCallback<const QVariant&>(callbackUndefined)); + QVERIFY(watcher.wait()); +} + +void tst_QWebEnginePage::fullScreenRequested() +{ + JavaScriptCallbackWatcher watcher; + QWebEnginePage* page = new QWebEnginePage; + QWebEngineView* view = new QWebEngineView; + view->setPage(page); + view->show(); + + page->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); + + QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool))); + page->load(QUrl("qrc:///resources/fullscreen.html")); + QTRY_COMPARE(loadSpy.count(), 1); + + page->runJavaScript("document.webkitFullscreenEnabled", JavaScriptCallback(true)); + page->runJavaScript("document.webkitIsFullScreen", JavaScriptCallback(false)); + QVERIFY(watcher.wait()); + + // FullscreenRequest must be a user gesture + bool acceptRequest = true; + connect(page, &QWebEnginePage::fullScreenRequested, + [&acceptRequest](const QWebEngineFullScreenRequest &request) { + if (acceptRequest) request.accept(); else request.reject(); + }); + + QTest::keyPress(qApp->focusWindow(), Qt::Key_Space); + QTest::qWait(100); + page->runJavaScript("document.webkitIsFullScreen", JavaScriptCallback(true)); + page->runJavaScript("document.webkitExitFullscreen()", JavaScriptCallbackUndefined()); + QVERIFY(watcher.wait()); + + acceptRequest = false; + + page->runJavaScript("document.webkitFullscreenEnabled", JavaScriptCallback(true)); + QTest::keyPress(qApp->focusWindow(), Qt::Key_Space); + QVERIFY(watcher.wait()); + page->runJavaScript("document.webkitIsFullScreen", JavaScriptCallback(false)); + QVERIFY(watcher.wait()); + + delete view; + delete page; +} + +void tst_QWebEnginePage::symmetricUrl() +{ + QWebEngineView view; + QSignalSpy loadFinishedSpy(view.page(), SIGNAL(loadFinished(bool))); + + QVERIFY(view.url().isEmpty()); + + QCOMPARE(view.history()->count(), 0); + + QUrl dataUrl("data:text/html,<h1>Test"); + + view.setUrl(dataUrl); + QCOMPARE(view.url(), dataUrl); + QCOMPARE(view.history()->count(), 0); + + // loading is _not_ immediate, so the text isn't set just yet. + QVERIFY(toPlainTextSync(view.page()).isEmpty()); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + + QCOMPARE(view.history()->count(), 1); + QCOMPARE(toPlainTextSync(view.page()), QString("Test")); + + QUrl dataUrl2("data:text/html,<h1>Test2"); + QUrl dataUrl3("data:text/html,<h1>Test3"); + + view.setUrl(dataUrl2); + view.setUrl(dataUrl3); + + QCOMPARE(view.url(), dataUrl3); + + QTRY_VERIFY(loadFinishedSpy.count() >= 2); + QTRY_COMPARE(loadFinishedSpy.count(), 3); + + QCOMPARE(view.history()->count(), 2); + + QCOMPARE(toPlainTextSync(view.page()), QString("Test3")); +} + +void tst_QWebEnginePage::progressSignal() +{ + QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); + + QUrl dataUrl("data:text/html,<h1>Test"); + m_view->setUrl(dataUrl); + + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + QVERIFY(progressSpy.size() >= 2); + int previousValue = -1; + for (QSignalSpy::ConstIterator it = progressSpy.begin(); it < progressSpy.end(); ++it) { + int current = (*it).first().toInt(); + QVERIFY(current >= previousValue); + previousValue = current; + } + + // But we always end at 100% + QCOMPARE(progressSpy.last().first().toInt(), 100); +} + +void tst_QWebEnginePage::urlChange() +{ + QSignalSpy urlSpy(m_page, SIGNAL(urlChanged(QUrl))); + + QUrl dataUrl("data:text/html,<h1>Test"); + m_view->setUrl(dataUrl); + + ::waitForSignal(m_page, SIGNAL(urlChanged(QUrl))); + + QCOMPARE(urlSpy.size(), 1); + + QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); + m_view->setUrl(dataUrl2); + + ::waitForSignal(m_page, SIGNAL(urlChanged(QUrl))); + + QCOMPARE(urlSpy.size(), 2); +} + +class FakeReply : public QNetworkReply { + Q_OBJECT + +public: + static const QUrl urlFor404ErrorWithoutContents; + + FakeReply(const QNetworkRequest& request, QObject* parent = 0) + : QNetworkReply(parent) + { + setOperation(QNetworkAccessManager::GetOperation); + setRequest(request); + setUrl(request.url()); + if (request.url() == QUrl("qrc:/test1.html")) { + setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); + setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); + QTimer::singleShot(0, this, SLOT(continueRedirect())); + } +#ifndef QT_NO_OPENSSL + else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) { + setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error!")); + QTimer::singleShot(0, this, SLOT(continueError())); + } +#endif + else if (request.url().host() == QLatin1String("abcdef.abcdef")) { + setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); + QTimer::singleShot(0, this, SLOT(continueError())); + } else if (request.url() == FakeReply::urlFor404ErrorWithoutContents) { + setError(QNetworkReply::ContentNotFoundError, "Not found"); + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 404); + QTimer::singleShot(0, this, SLOT(continueError())); + } + + open(QIODevice::ReadOnly); + } + ~FakeReply() + { + close(); + } + virtual void abort() {} + virtual void close() {} + +protected: + qint64 readData(char*, qint64) + { + return 0; + } + +private Q_SLOTS: + void continueRedirect() + { + emit metaDataChanged(); + emit finished(); + } + + void continueError() + { + emit error(this->error()); + emit finished(); + } +}; + +const QUrl FakeReply::urlFor404ErrorWithoutContents = QUrl("http://this.will/return-http-404-error-without-contents.html"); + +class FakeNetworkManager : public QNetworkAccessManager { + Q_OBJECT + +public: + FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } + +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) + { + QString url = request.url().toString(); + if (op == QNetworkAccessManager::GetOperation) { +#ifndef QT_NO_OPENSSL + if (url == "qrc:/fake-ssl-error.html") { + FakeReply* reply = new FakeReply(request, this); + QList<QSslError> errors; + emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); + return reply; + } +#endif + if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/" || request.url() == FakeReply::urlFor404ErrorWithoutContents) + return new FakeReply(request, this); + } + + return QNetworkAccessManager::createRequest(op, request, outgoingData); + } +}; + +void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() +{ + QWebEnginePage page; + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + const QUrl first("http://abcdef.abcdef/"); + page.setUrl(first); + ::waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(spy.count(), 1); + QCOMPARE(page.url(), first); + QCOMPARE(page.requestedUrl(), first); + QVERIFY(!spy.at(0).first().toBool()); + + const QUrl second("http://abcdef.abcdef/another_page.html"); + QVERIFY(first != second); + + page.load(second); + ::waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(spy.count(), 2); + QCOMPARE(page.url(), first); + QCOMPARE(page.requestedUrl(), second); + QVERIFY(!spy.at(1).first().toBool()); +} + +void tst_QWebEnginePage::javaScriptWindowObjectCleared_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<int>("signalCount"); + QTest::newRow("with <script>") << "<html><body><script>i=0</script><p>hello world</p></body></html>" << 1; + // NOTE: Empty scripts no longer cause this signal to be emitted. + QTest::newRow("with empty <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 0; + QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; +} + +void tst_QWebEnginePage::javaScriptWindowObjectCleared() +{ +#if !defined(QWEBENGINEPAGE_JAVASCRIPTWINDOWOBJECTCLEARED) + QSKIP("QWEBENGINEPAGE_JAVASCRIPTWINDOWOBJECTCLEARED"); +#else + QWebEnginePage page; + QSignalSpy spy(&page, SIGNAL(javaScriptWindowObjectCleared())); + QFETCH(QString, html); + page.setHtml(html); + + QFETCH(int, signalCount); + QCOMPARE(spy.count(), signalCount); +#endif +} + +void tst_QWebEnginePage::javaScriptWindowObjectClearedOnEvaluate() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + QWebEnginePage page; + QSignalSpy spy(&page, SIGNAL(javaScriptWindowObjectCleared())); + page.setHtml("<html></html>"); + QCOMPARE(spy.count(), 0); + page.evaluateJavaScript("var a = 'a';"); + QCOMPARE(spy.count(), 1); + // no new clear for a new script: + page.evaluateJavaScript("var a = 1;"); + QCOMPARE(spy.count(), 1); +#endif +} + +void tst_QWebEnginePage::asyncAndDelete() +{ + QWebEnginePage *page = new QWebEnginePage; + CallbackSpy<QString> plainTextSpy; + CallbackSpy<QString> htmlSpy; + page->toPlainText(plainTextSpy.ref()); + page->toHtml(htmlSpy.ref()); + + delete page; + // Pending callbacks should be called with an empty value in the page's destructor. + QCOMPARE(plainTextSpy.waitForResult(), QString()); + QVERIFY(plainTextSpy.wasCalled()); + QCOMPARE(htmlSpy.waitForResult(), QString()); + QVERIFY(htmlSpy.wasCalled()); +} + +void tst_QWebEnginePage::earlyToHtml() +{ + QString html("<html><head></head><body></body></html>"); + QCOMPARE(toHtmlSync(m_view->page()), html); +} + +void tst_QWebEnginePage::setHtml() +{ + QString html("<html><head></head><body><p>hello world</p></body></html>"); + QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool))); + m_view->page()->setHtml(html); + QVERIFY(spy.wait()); + QCOMPARE(toHtmlSync(m_view->page()), html); +} + +void tst_QWebEnginePage::setHtmlWithImageResource() +{ + // By default, only security origins of local files can load local resources. + // So we should specify baseUrl to be a local file in order to get a proper origin and load the local image. + + QLatin1String html("<html><body><p>hello world</p><img src='qrc:/resources/image.png'/></body></html>"); + QWebEnginePage page; + + page.setHtml(html, QUrl(QLatin1String("file:///path/to/file"))); + waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); + + QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); + + // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. + + page.setHtml(html); + waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); + QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 0); + QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 0); +} + +void tst_QWebEnginePage::setHtmlWithStylesheetResource() +{ +#if !defined(QWEBENGINEELEMENT) + QSKIP("QWEBENGINEELEMENT"); +#else + // By default, only security origins of local files can load local resources. + // So we should specify baseUrl to be a local file in order to be able to download the local stylesheet. + + const char* htmlData = + "<html>" + "<head>" + "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" + "</head>" + "<body>" + "<p id='idP'>some text</p>" + "</body>" + "</html>"; + QLatin1String html(htmlData); + QWebEnginePage page; + QWebEngineElement webElement; + + page.setHtml(html, QUrl(QLatin1String("qrc:///file"))); + waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); + webElement = page.documentElement().findFirst("p"); + QCOMPARE(webElement.styleProperty("color", QWebEngineElement::CascadedStyle), QLatin1String("red")); + + // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. + + page.setHtml(html, QUrl(QLatin1String("http://www.example.com/"))); + waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); + webElement = page.documentElement().findFirst("p"); + QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue); + QCOMPARE(webElement.styleProperty("color", QWebEngineElement::CascadedStyle), QString()); +#endif +} + +void tst_QWebEnginePage::setHtmlWithBaseURL() +{ + // This tests if baseUrl is indeed affecting the relative paths from resources. + // As we are using a local file as baseUrl, its security origin should be able to load local resources. + + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QDir::setCurrent(TESTS_SOURCE_DIR); + + QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); + + QWebEnginePage page; + + // in few seconds, the image should be completey loaded + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); + waitForSignal(&page, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 1); + + QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); + QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); + + // no history item has to be added. + QCOMPARE(m_view->page()->history()->count(), 0); +} + +class MyPage : public QWebEnginePage +{ +public: + MyPage() : QWebEnginePage(), alerts(0) {} + int alerts; + +protected: + virtual void javaScriptAlert(const QUrl &securityOrigin, const QString &msg) + { + alerts++; + QCOMPARE(securityOrigin, QUrl(QStringLiteral("http://test.origin.com/"))); + QCOMPARE(msg, QString("foo")); + } +}; + +void tst_QWebEnginePage::setHtmlWithJSAlert() +{ + QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>"); + MyPage page; + page.setHtml(html, QUrl(QStringLiteral("http://test.origin.com/path#fragment"))); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(page.alerts, 1); + QCOMPARE(toHtmlSync(&page), html); +} + +void tst_QWebEnginePage::metaData() +{ +#if !defined(QWEBENGINEPAGE_METADATA) + QSKIP("QWEBENGINEPAGE_METADATA"); +#else + m_view->setHtml("<html>" + " <head>" + " <meta name=\"description\" content=\"Test description\">" + " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" + " </head>" + "</html>"); + + QMultiMap<QString, QString> metaData = m_view->page()->metaData(); + + QCOMPARE(metaData.count(), 2); + + QCOMPARE(metaData.value("description"), QString("Test description")); + QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); + QCOMPARE(metaData.value("nonexistent"), QString()); + + m_view->setHtml("<html>" + " <head>" + " <meta name=\"samekey\" content=\"FirstValue\">" + " <meta name=\"samekey\" content=\"SecondValue\">" + " </head>" + "</html>"); + + metaData = m_view->page()->metaData(); + + QCOMPARE(metaData.count(), 2); + + QStringList values = metaData.values("samekey"); + QCOMPARE(values.count(), 2); + + QVERIFY(values.contains("FirstValue")); + QVERIFY(values.contains("SecondValue")); + + QCOMPARE(metaData.value("nonexistent"), QString()); +#endif +} + +#if !defined(QT_NO_COMBOBOX) +void tst_QWebEnginePage::popupFocus() +{ +#if !defined(QWEBENGINEELEMENT) + QSKIP("QWEBENGINEELEMENT"); +#else + QWebEngineView view; + view.setHtml("<html>" + " <body>" + " <select name=\"select\">" + " <option>1</option>" + " <option>2</option>" + " </select>" + " <input type=\"text\"> </input>" + " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" + "This test checks whether showing and hiding a popup" + "takes the focus away from the webpage." + " </textarea>" + " </body>" + "</html>"); + view.resize(400, 100); + // Call setFocus before show to work around http://bugreports.qt.nokia.com/browse/QTBUG-14762 + view.setFocus(); + view.show(); + QTest::qWaitForWindowExposed(&view); + view.activateWindow(); + QTRY_VERIFY(view.hasFocus()); + + // open the popup by clicking. check if focus is on the popup + const QWebEngineElement webCombo = view.page()->documentElement().findFirst(QLatin1String("select[name=select]")); + QTest::mouseClick(&view, Qt::LeftButton, 0, webCombo.geometry().center()); + + QComboBox* combo = view.findChild<QComboBox*>(); + QVERIFY(combo != 0); + QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup + + // hide the popup and check if focus is on the page + combo->hidePopup(); + QTRY_VERIFY(view.hasFocus()); // Focus should be back on the WebView +#endif +} +#endif + +void tst_QWebEnginePage::inputFieldFocus() +{ +#if !defined(QWEBENGINEELEMENT) + QSKIP("QWEBENGINEELEMENT"); +#else + QWebEngineView view; + view.setHtml("<html><body><input type=\"text\"></input></body></html>"); + view.resize(400, 100); + view.show(); + QTest::qWaitForWindowExposed(&view); + view.activateWindow(); + view.setFocus(); + QTRY_VERIFY(view.hasFocus()); + + // double the flashing time, should at least blink once already + int delay = qApp->cursorFlashTime() * 2; + + // focus the lineedit and check if it blinks + bool autoSipEnabled = qApp->autoSipEnabled(); + qApp->setAutoSipEnabled(false); + const QWebEngineElement inputElement = view.page()->documentElement().findFirst(QLatin1String("input[type=text]")); + QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); + m_inputFieldsTestView = &view; + view.installEventFilter( this ); + QTest::qWait(delay); + QVERIFY2(m_inputFieldTestPaintCount >= 3, + "The input field should have a blinking caret"); + qApp->setAutoSipEnabled(autoSipEnabled); +#endif +} + +void tst_QWebEnginePage::hitTestContent() +{ +#if !defined(QWEBENGINEELEMENT) + QSKIP("QWEBENGINEELEMENT"); +#else + QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); + + QWebEnginePage page; + page.setHtml(html); + page.setViewportSize(QSize(200, 0)); //no height so link is not visible + const QWebEngineElement linkElement = page.documentElement().findFirst(QLatin1String("a#link")); + QWebEngineHitTestResult result = page.hitTestContent(linkElement.geometry().center()); + QCOMPARE(result.linkText(), QString("link text")); + QWebEngineElement link = result.linkElement(); + QCOMPARE(link.attribute("target"), QString("_foo")); +#endif +} + +void tst_QWebEnginePage::baseUrl_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<QUrl>("loadUrl"); + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("baseUrl"); + + QTest::newRow("null") << QString() << QUrl() + << QUrl("about:blank") << QUrl("about:blank"); + + QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") + << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); + + QString html = "<html>" + "<head>" + "<base href=\"http://foobaz.bar/\" />" + "</head>" + "</html>"; + QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") + << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); +} + +void tst_QWebEnginePage::baseUrl() +{ + QFETCH(QString, html); + QFETCH(QUrl, loadUrl); + QFETCH(QUrl, url); + QFETCH(QUrl, baseUrl); + + QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); + m_page->setHtml(html, loadUrl); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(m_page->url(), url); + QEXPECT_FAIL("null", "Slight change: We now translate QUrl() to about:blank for the virtual url, but not for the baseUrl", Continue); + QCOMPARE(baseUrlSync(m_page), baseUrl); +} + +class DummyPaintEngine: public QPaintEngine { +public: + + DummyPaintEngine() + : QPaintEngine(QPaintEngine::AllFeatures) + , renderHints(0) + { + } + + bool begin(QPaintDevice*) + { + setActive(true); + return true; + } + + bool end() + { + setActive(false); + return false; + } + + void updateState(const QPaintEngineState& state) + { + renderHints = state.renderHints(); + } + + void drawPath(const QPainterPath&) { } + void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } + + QPaintEngine::Type type() const + { + return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); + } + + QPainter::RenderHints renderHints; +}; + +class DummyPaintDevice: public QPaintDevice { +public: + DummyPaintDevice() + : QPaintDevice() + , m_engine(new DummyPaintEngine) + { + } + + ~DummyPaintDevice() + { + delete m_engine; + } + + QPaintEngine* paintEngine() const + { + return m_engine; + } + + QPainter::RenderHints renderHints() const + { + return m_engine->renderHints; + } + +protected: + int metric(PaintDeviceMetric metric) const; + +private: + DummyPaintEngine* m_engine; + friend class DummyPaintEngine; +}; + + +int DummyPaintDevice::metric(PaintDeviceMetric metric) const +{ + switch (metric) { + case PdmWidth: + return 400; + break; + + case PdmHeight: + return 200; + break; + + case PdmNumColors: + return INT_MAX; + break; + + case PdmDepth: + return 32; + break; + + default: + break; + } + return 0; +} + +void tst_QWebEnginePage::renderHints() +{ +#if !defined(QWEBENGINEPAGE_RENDER) + QSKIP("QWEBENGINEPAGE_RENDER"); +#else + QString html("<html><body><p>Hello, world!</p></body></html>"); + + QWebEnginePage page; + page.setHtml(html); + page.setViewportSize(page.contentsSize()); + + // We will call frame->render and trap the paint engine state changes + // to ensure that GraphicsContext does not clobber the render hints. + DummyPaintDevice buffer; + QPainter painter(&buffer); + + painter.setRenderHint(QPainter::TextAntialiasing, false); + page.render(&painter); + QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); + QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::TextAntialiasing, true); + page.render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + page.render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::HighQualityAntialiasing, true); + page.render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); +#endif +} + +void tst_QWebEnginePage::scrollPosition() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + // enlarged image in a small viewport, to provoke the scrollbars to appear + QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); + + QWebEnginePage page; + page.setViewportSize(QSize(200, 200)); + + page.setHtml(html); + page.setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + page.setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + + // try to set the scroll offset programmatically + page.setScrollPosition(QPoint(23, 29)); + QCOMPARE(page.scrollPosition().x(), 23); + QCOMPARE(page.scrollPosition().y(), 29); + + int x = page.evaluateJavaScript("window.scrollX").toInt(); + int y = page.evaluateJavaScript("window.scrollY").toInt(); + QCOMPARE(x, 23); + QCOMPARE(y, 29); +#endif +} + +void tst_QWebEnginePage::scrollToAnchor() +{ +#if !defined(QWEBENGINEELEMENT) + QSKIP("QWEBENGINEELEMENT"); +#else + QWebEnginePage page; + page.setViewportSize(QSize(480, 800)); + + QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>" + "<p><a id=\"foo\">This</a> is an anchor</p>" + "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>" + "</body></html>"); + page.setHtml(html); + page.setScrollPosition(QPoint(0, 0)); + QCOMPARE(page.scrollPosition().x(), 0); + QCOMPARE(page.scrollPosition().y(), 0); + + QWebEngineElement fooAnchor = page.findFirstElement("a[id=foo]"); + + page.scrollToAnchor("foo"); + QCOMPARE(page.scrollPosition().y(), fooAnchor.geometry().top()); + + page.scrollToAnchor("bar"); + page.scrollToAnchor("foo"); + QCOMPARE(page.scrollPosition().y(), fooAnchor.geometry().top()); + + page.scrollToAnchor("top"); + QCOMPARE(page.scrollPosition().y(), 0); + + page.scrollToAnchor("bar"); + page.scrollToAnchor("notexist"); + QVERIFY(page.scrollPosition().y() != 0); +#endif +} + + +void tst_QWebEnginePage::scrollbarsOff() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + QWebEngineView view; + QWebEngineFrame* mainFrame = view.page(); + + mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + + QString html("<script>" \ + " function checkScrollbar() {" \ + " if (innerWidth === document.documentElement.offsetWidth)" \ + " document.getElementById('span1').innerText = 'SUCCESS';" \ + " else" \ + " document.getElementById('span1').innerText = 'FAIL';" \ + " }" \ + "</script>" \ + "<body>" \ + " <div style='margin-top:1000px ; margin-left:1000px'>" \ + " <a id='offscreen' href='a'>End</a>" \ + " </div>" \ + "<span id='span1'></span>" \ + "</body>"); + + + QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + view.setHtml(html); + ::waitForSignal(&view, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(loadSpy.count(), 1); + + mainFrame->evaluateJavaScript("checkScrollbar();"); + QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS")); +#endif +} + +void tst_QWebEnginePage::horizontalScrollAfterBack() +{ +#if !defined(QWEBENGINESETTINGS) + QSKIP("QWEBENGINESETTINGS"); +#else + QWebEngineView view; + QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); + + view.page()->settings()->setMaximumPagesInCache(2); + view.page()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); + view.page()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); + + view.load(QUrl("qrc:/resources/testiframe2.html")); + view.resize(200, 200); + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); + + view.load(QUrl("qrc:/resources/testiframe.html")); + QTRY_COMPARE(loadSpy.count(), 2); + + view.page()->triggerAction(QWebEnginePage::Back); + QTRY_COMPARE(loadSpy.count(), 3); + QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); +#endif +} + +void tst_QWebEnginePage::evaluateWillCauseRepaint() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + QWebEngineView view; + QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" + "junk</div>bottom</body></html>"); + view.setHtml(html); + view.show(); + + QTest::qWaitForWindowExposed(&view); + view.page()->evaluateJavaScript( + "document.getElementById('junk').style.display = 'none';"); + + ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); +#endif +} + +void tst_QWebEnginePage::setContent_data() +{ + QTest::addColumn<QString>("mimeType"); + QTest::addColumn<QByteArray>("testContents"); + QTest::addColumn<QString>("expected"); + + QString str = QString::fromUtf8("ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει"); + QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str; + + QTextCodec *utf16 = QTextCodec::codecForName("UTF-16"); + if (utf16) + QTest::newRow("UTF-16 plain text") << "text/plain; charset=utf-16" << utf16->fromUnicode(str) << str; + + str = QString::fromUtf8("Une chaîne de caractères à sa façon."); + QTest::newRow("latin-1 plain text") << "text/plain; charset=iso-8859-1" << str.toLatin1() << str; + + +} + +void tst_QWebEnginePage::setContent() +{ + QFETCH(QString, mimeType); + QFETCH(QByteArray, testContents); + QFETCH(QString, expected); + QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); + m_view->setContent(testContents, mimeType); + QVERIFY(loadSpy.wait()); + QCOMPARE(toPlainTextSync(m_view->page()), expected); +} + +class CacheNetworkAccessManager : public QNetworkAccessManager { +public: + CacheNetworkAccessManager(QObject* parent = 0) + : QNetworkAccessManager(parent) + , m_lastCacheLoad(QNetworkRequest::PreferNetwork) + { + } + + virtual QNetworkReply* createRequest(Operation, const QNetworkRequest& request, QIODevice*) + { + QVariant cacheLoad = request.attribute(QNetworkRequest::CacheLoadControlAttribute); + if (cacheLoad.isValid()) + m_lastCacheLoad = static_cast<QNetworkRequest::CacheLoadControl>(cacheLoad.toUInt()); + else + m_lastCacheLoad = QNetworkRequest::PreferNetwork; // default value + return new FakeReply(request, this); + } + + QNetworkRequest::CacheLoadControl lastCacheLoad() const + { + return m_lastCacheLoad; + } + +private: + QNetworkRequest::CacheLoadControl m_lastCacheLoad; +}; + +void tst_QWebEnginePage::setCacheLoadControlAttribute() +{ +#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); +#else + QWebEnginePage page; + CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); + page.setNetworkAccessManager(manager); + + QNetworkRequest request(QUrl("http://abcdef.abcdef/")); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); + page.load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + page.load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + page.load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); + page.load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); +#endif +} + +void tst_QWebEnginePage::setUrlWithPendingLoads() +{ + QWebEnginePage page; + page.setHtml("<img src='dummy:'/>"); + page.setUrl(QUrl("about:blank")); +} + +void tst_QWebEnginePage::setUrlToEmpty() +{ + QSKIP("FIXME: [0908/090526:FATAL:navigation_controller_impl.cc(927)] Check failed: active_entry->site_instance() == rfh->GetSiteInstance()."); + + int expectedLoadFinishedCount = 0; + const QUrl aboutBlank("about:blank"); + const QUrl url("qrc:/resources/test2.html"); + + QWebEnginePage page; + QCOMPARE(page.url(), QUrl()); + QCOMPARE(page.requestedUrl(), QUrl()); + QCOMPARE(baseUrlSync(&page), QUrl()); + + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + // Set existing url + page.setUrl(url); + expectedLoadFinishedCount++; + ::waitForSignal(&page, SIGNAL(loadFinished(bool))); + + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(page.url(), url); + QCOMPARE(page.requestedUrl(), url); + QCOMPARE(baseUrlSync(&page), url); + + // Set empty url + page.setUrl(QUrl()); + expectedLoadFinishedCount++; + + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(page.url(), aboutBlank); + QCOMPARE(page.requestedUrl(), QUrl()); + QCOMPARE(baseUrlSync(&page), aboutBlank); + + // Set existing url + page.setUrl(url); + expectedLoadFinishedCount++; + + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(page.url(), url); + QCOMPARE(page.requestedUrl(), url); + QCOMPARE(baseUrlSync(&page), url); + + // Load empty url + page.load(QUrl()); + expectedLoadFinishedCount++; + + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(page.url(), aboutBlank); + QCOMPARE(page.requestedUrl(), QUrl()); + QCOMPARE(baseUrlSync(&page), aboutBlank); +} + +void tst_QWebEnginePage::setUrlToInvalid() +{ + QEXPECT_FAIL("", "Unsupported: QtWebEngine doesn't adjust invalid URLs.", Abort); + QVERIFY(false); + + QWebEnginePage page; + + const QUrl invalidUrl("http:/example.com"); + QVERIFY(!invalidUrl.isEmpty()); + QVERIFY(invalidUrl != QUrl()); + + // QWebEnginePage will do its best to accept the URL, possible converting it to a valid equivalent URL. + const QUrl validUrl("http://example.com/"); + page.setUrl(invalidUrl); + QCOMPARE(page.url(), validUrl); + QCOMPARE(page.requestedUrl(), validUrl); + QCOMPARE(baseUrlSync(&page), validUrl); + + // QUrls equivalent to QUrl() will be treated as such. + const QUrl aboutBlank("about:blank"); + const QUrl anotherInvalidUrl("1http://bugs.webkit.org"); + QVERIFY(!anotherInvalidUrl.isEmpty()); // and they are not necessarily empty. + QVERIFY(!anotherInvalidUrl.isValid()); + QCOMPARE(anotherInvalidUrl.toEncoded(), QUrl().toEncoded()); + + page.setUrl(anotherInvalidUrl); + QCOMPARE(page.url(), aboutBlank); + QCOMPARE(page.requestedUrl().toEncoded(), anotherInvalidUrl.toEncoded()); + QCOMPARE(baseUrlSync(&page), aboutBlank); +} + +static QStringList collectHistoryUrls(QWebEngineHistory *history) +{ + QStringList urls; + foreach (const QWebEngineHistoryItem &i, history->items()) + urls << i.url().toString(); + return urls; +} + +void tst_QWebEnginePage::setUrlHistory() +{ + QSKIP("FIXME: [0908/090526:FATAL:navigation_controller_impl.cc(927)] Check failed: active_entry->site_instance() == rfh->GetSiteInstance()."); + + const QUrl aboutBlank("about:blank"); + QUrl url; + int expectedLoadFinishedCount = 0; + QSignalSpy spy(m_page, SIGNAL(loadFinished(bool))); + + QCOMPARE(m_page->history()->count(), 0); + + m_page->setUrl(QUrl()); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(m_page->url(), aboutBlank); + QCOMPARE(m_page->requestedUrl(), QUrl()); + // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString()); + + url = QUrl("http://non.existent/"); + m_page->setUrl(url); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + // When error page is disabled in case of LoadFail the entry of the unavailable page is not stored. + // We expect the url of the previously loaded page here. + QCOMPARE(m_page->url(), aboutBlank); + QCOMPARE(m_page->requestedUrl(), QUrl()); + // Since the entry of the unavailable page is not stored it will not available in the history. + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString()); + + url = QUrl("qrc:/resources/test1.html"); + m_page->setUrl(url); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->requestedUrl(), url); + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString() << QStringLiteral("qrc:/resources/test1.html")); + + m_page->setUrl(QUrl()); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(m_page->url(), aboutBlank); + QCOMPARE(m_page->requestedUrl(), QUrl()); + // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() + << aboutBlank.toString() + << QStringLiteral("qrc:/resources/test1.html") + << aboutBlank.toString()); + + url = QUrl("qrc:/resources/test1.html"); + m_page->setUrl(url); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->requestedUrl(), url); + // The history count DOES change since the about:blank is in the list. + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() + << aboutBlank.toString() + << QStringLiteral("qrc:/resources/test1.html") + << aboutBlank.toString() + << QStringLiteral("qrc:/resources/test1.html")); + + url = QUrl("qrc:/resources/test2.html"); + m_page->setUrl(url); + expectedLoadFinishedCount++; + QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->requestedUrl(), url); + QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() + << aboutBlank.toString() + << QStringLiteral("qrc:/resources/test1.html") + << aboutBlank.toString() + << QStringLiteral("qrc:/resources/test1.html") + << QStringLiteral("qrc:/resources/test2.html")); +} + +void tst_QWebEnginePage::setUrlUsingStateObject() +{ + const QUrl aboutBlank("about:blank"); + QUrl url; + QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl))); + int expectedUrlChangeCount = 0; + + QCOMPARE(m_page->history()->count(), 0); + + url = QUrl("qrc:/resources/test1.html"); + m_page->setUrl(url); + waitForSignal(m_page, SIGNAL(loadFinished(bool))); + expectedUrlChangeCount++; + QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->history()->count(), 1); + + evaluateJavaScriptSync(m_page, "window.history.pushState(null, 'push', 'navigate/to/here')"); + expectedUrlChangeCount++; + QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/here")); + QCOMPARE(m_page->history()->count(), 2); + QVERIFY(m_page->history()->canGoBack()); + + evaluateJavaScriptSync(m_page, "window.history.replaceState(null, 'replace', 'another/location')"); + expectedUrlChangeCount++; + QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/another/location")); + QCOMPARE(m_page->history()->count(), 2); + QVERIFY(!m_page->history()->canGoForward()); + QVERIFY(m_page->history()->canGoBack()); + + evaluateJavaScriptSync(m_page, "window.history.back()"); QTest::qWait(100); + expectedUrlChangeCount++; + QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QCOMPARE(m_page->url(), QUrl("qrc:/resources/test1.html")); + QVERIFY(m_page->history()->canGoForward()); + QVERIFY(!m_page->history()->canGoBack()); +} + +static inline QUrl extractBaseUrl(const QUrl& url) +{ + return url.resolved(QUrl()); +} + +void tst_QWebEnginePage::setUrlThenLoads_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("baseUrl"); + + QTest::newRow("resource file") << QUrl("qrc:/resources/test1.html") << extractBaseUrl(QUrl("qrc:/resources/test1.html")); + QTest::newRow("base specified in HTML") << QUrl("data:text/html,<head><base href=\"http://different.base/\"></head>") << QUrl("http://different.base/"); +} + +void tst_QWebEnginePage::setUrlThenLoads() +{ + QFETCH(QUrl, url); + QFETCH(QUrl, baseUrl); + QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl))); + QSignalSpy startedSpy(m_page, SIGNAL(loadStarted())); + QSignalSpy finishedSpy(m_page, SIGNAL(loadFinished(bool))); + + m_page->setUrl(url); + QTRY_COMPARE(startedSpy.count(), 1); + QTRY_COMPARE(urlChangedSpy.count(), 1); + QTRY_COMPARE(finishedSpy.count(), 1); + QVERIFY(finishedSpy.at(0).first().toBool()); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->requestedUrl(), url); + QCOMPARE(baseUrlSync(m_page), baseUrl); + + const QUrl urlToLoad1("qrc:/resources/test2.html"); + const QUrl urlToLoad2("qrc:/resources/test1.html"); + + // Just after first load. URL didn't changed yet. + m_page->load(urlToLoad1); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->requestedUrl(), urlToLoad1); + // baseUrlSync spins an event loop and this sometimes return the next result. + // QCOMPARE(baseUrlSync(m_page), baseUrl); + QTRY_COMPARE(startedSpy.count(), 2); + + // After first URL changed. + QTRY_COMPARE(urlChangedSpy.count(), 2); + QTRY_COMPARE(finishedSpy.count(), 2); + QVERIFY(finishedSpy.at(1).first().toBool()); + QCOMPARE(m_page->url(), urlToLoad1); + QCOMPARE(m_page->requestedUrl(), urlToLoad1); + QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); + + // Just after second load. URL didn't changed yet. + m_page->load(urlToLoad2); + QCOMPARE(m_page->url(), urlToLoad1); + QCOMPARE(m_page->requestedUrl(), urlToLoad2); + QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); + QTRY_COMPARE(startedSpy.count(), 3); + + // After second URL changed. + QTRY_COMPARE(urlChangedSpy.count(), 3); + QTRY_COMPARE(finishedSpy.count(), 3); + QVERIFY(finishedSpy.at(2).first().toBool()); + QCOMPARE(m_page->url(), urlToLoad2); + QCOMPARE(m_page->requestedUrl(), urlToLoad2); + QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad2)); +} + +void tst_QWebEnginePage::loadFinishedAfterNotFoundError() +{ + QWebEnginePage page; + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + page.setUrl(QUrl("http://non.existent/url")); + QTRY_COMPARE(spy.count(), 1); + + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); + page.setUrl(QUrl("http://another.non.existent/url")); + QTRY_COMPARE(spy.count(), 2); +} + +class URLSetter : public QObject { + Q_OBJECT + +public: + enum Signal { + LoadStarted, + LoadFinished, + ProvisionalLoad + }; + + enum Type { + UseLoad, + UseSetUrl + }; + + URLSetter(QWebEnginePage*, Signal, Type, const QUrl&); + +public Q_SLOTS: + void execute(); + +Q_SIGNALS: + void finished(); + +private: + QWebEnginePage* m_page; + QUrl m_url; + Type m_type; +}; + +Q_DECLARE_METATYPE(URLSetter::Signal) +Q_DECLARE_METATYPE(URLSetter::Type) + +URLSetter::URLSetter(QWebEnginePage* page, Signal signal, URLSetter::Type type, const QUrl& url) + : m_page(page), m_url(url), m_type(type) +{ + if (signal == LoadStarted) + connect(m_page, SIGNAL(loadStarted()), SLOT(execute())); + else if (signal == LoadFinished) + connect(m_page, SIGNAL(loadFinished(bool)), SLOT(execute())); + else + connect(m_page, SIGNAL(provisionalLoad()), SLOT(execute())); +} + +void URLSetter::execute() +{ + // We track only the first emission. + m_page->disconnect(this); + if (m_type == URLSetter::UseLoad) + m_page->load(m_url); + else + m_page->setUrl(m_url); + connect(m_page, SIGNAL(loadFinished(bool)), SIGNAL(finished())); +} + +void tst_QWebEnginePage::loadInSignalHandlers_data() +{ + QSKIP("FIXME: This crashes in content::WebContentsImpl::NavigateToEntry because of reentrancy. Should we require QueuedConnections or do it ourselves to support this?"); + + QTest::addColumn<URLSetter::Type>("type"); + QTest::addColumn<URLSetter::Signal>("signal"); + QTest::addColumn<QUrl>("url"); + + const QUrl validUrl("qrc:/resources/test2.html"); + const QUrl invalidUrl("qrc:/invalid"); + + QTest::newRow("call load() in loadStarted() after valid url") << URLSetter::UseLoad << URLSetter::LoadStarted << validUrl; + QTest::newRow("call load() in loadStarted() after invalid url") << URLSetter::UseLoad << URLSetter::LoadStarted << invalidUrl; + QTest::newRow("call load() in loadFinished() after valid url") << URLSetter::UseLoad << URLSetter::LoadFinished << validUrl; + QTest::newRow("call load() in loadFinished() after invalid url") << URLSetter::UseLoad << URLSetter::LoadFinished << invalidUrl; + QTest::newRow("call load() in provisionalLoad() after valid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << validUrl; + QTest::newRow("call load() in provisionalLoad() after invalid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << invalidUrl; + + QTest::newRow("call setUrl() in loadStarted() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << validUrl; + QTest::newRow("call setUrl() in loadStarted() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << invalidUrl; + QTest::newRow("call setUrl() in loadFinished() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << validUrl; + QTest::newRow("call setUrl() in loadFinished() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << invalidUrl; + QTest::newRow("call setUrl() in provisionalLoad() after valid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << validUrl; + QTest::newRow("call setUrl() in provisionalLoad() after invalid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << invalidUrl; +} + +void tst_QWebEnginePage::loadInSignalHandlers() +{ + QFETCH(URLSetter::Type, type); + QFETCH(URLSetter::Signal, signal); + QFETCH(QUrl, url); + + const QUrl urlForSetter("qrc:/resources/test1.html"); + URLSetter setter(m_page, signal, type, urlForSetter); + + m_page->load(url); + waitForSignal(&setter, SIGNAL(finished()), 200); + QCOMPARE(m_page->url(), urlForSetter); } QTEST_MAIN(tst_QWebEnginePage) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc index 994d71b43..c7bffd5bb 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc @@ -1,5 +1,6 @@ <!DOCTYPE RCC><RCC version="1.0"> <qresource> + <file>resources/content.html</file> <file>resources/index.html</file> <file>resources/frame_a.html</file> <file>resources/frame_c.html</file> @@ -7,8 +8,14 @@ <file>resources/iframe2.html</file> <file>resources/iframe3.html</file> <file>resources/framedindex.html</file> - <file>resources/content.html</file> + <file>resources/fullscreen.html</file> <file>resources/script.html</file> <file>resources/user.css</file> + <file>resources/image.png</file> + <file>resources/style.css</file> + <file>resources/test1.html</file> + <file>resources/test2.html</file> + <file>resources/testiframe.html</file> + <file>resources/testiframe2.html</file> </qresource> </RCC> diff --git a/tests/auto/widgets/qwebenginescript/qwebenginescript.pro b/tests/auto/widgets/qwebenginescript/qwebenginescript.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebenginescript/qwebenginescript.pro +++ b/tests/auto/widgets/qwebenginescript/qwebenginescript.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebengineview/qwebengineview.pro b/tests/auto/widgets/qwebengineview/qwebengineview.pro index ff6c49628..e99c7f493 100644 --- a/tests/auto/widgets/qwebengineview/qwebengineview.pro +++ b/tests/auto/widgets/qwebengineview/qwebengineview.pro @@ -1,2 +1 @@ include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 83f65f9d0..1ebb22cc9 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -25,6 +25,7 @@ #include <qpainter.h> #include <qwebengineview.h> #include <qwebenginepage.h> +#include <qwebenginesettings.h> #include <qnetworkrequest.h> #include <qdiriterator.h> @@ -148,9 +149,7 @@ void tst_QWebEngineView::reusePage() QWebEngineView* view1 = new QWebEngineView; QPointer<QWebEnginePage> page = new QWebEnginePage; view1->setPage(page.data()); -#if defined(QWEBENGINESETTINGS) page.data()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); -#endif page->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); if (html.contains("</embed>")) { // some reasonable time for the PluginStream to feed test.swf to flash and start painting diff --git a/tests/auto/widgets/tests.pri b/tests/auto/widgets/tests.pri index 8d86ac93e..afdf46f42 100644 --- a/tests/auto/widgets/tests.pri +++ b/tests/auto/widgets/tests.pri @@ -11,6 +11,8 @@ TARGET = tst_$$TARGET SOURCES += $${TARGET}.cpp INCLUDEPATH += $$PWD +exists($$_PRO_FILE_PWD_/$${TARGET}.qrc): RESOURCES += $${TARGET}.qrc + QT += testlib network webenginewidgets widgets macx: CONFIG -= app_bundle diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h index 83067fe8d..2b485fc0f 100644 --- a/tests/auto/widgets/util.h +++ b/tests/auto/widgets/util.h @@ -166,6 +166,9 @@ static inline QUrl baseUrlSync(QWebEnginePage *page) #define W_QSKIP(a, b) QSKIP(a) #define W_QTEST_MAIN(TestObject, params) \ +QT_BEGIN_NAMESPACE \ +QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS \ +QT_END_NAMESPACE \ int main(int argc, char *argv[]) \ { \ QVector<const char *> w_argv(argc); \ @@ -178,6 +181,8 @@ int main(int argc, char *argv[]) \ QApplication app(w_argc, const_cast<char **>(w_argv.data())); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ QTEST_DISABLE_KEYPAD_NAVIGATION \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ TestObject tc; \ + QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ } diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 2c8296cb8..aaafcb6e5 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -4,7 +4,6 @@ CONFIG += ordered SUBDIRS += \ qwebengineaccessibility \ - qwebengineframe \ qwebenginepage \ qwebenginehistory \ qwebenginehistoryinterface \ |