summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2013-08-07 11:27:50 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2013-08-20 18:15:34 +0200
commitf95e0645b262ccd7e96a38964145e1580337e76c (patch)
tree61f3797e1e26fc72fac08d0a1cdd5c4c0999f8d6 /tests
parent63c45a18d37350e5455cf2869832e8b1743712a2 (diff)
Import relevant QtWebKit API tests.
This import the tests as-is, without adding them to the build. Change-Id: I8b8e11a9f62b2a8f2be299347d2621b10fb61f20 Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/widgets/qwebengineframe/qwebengineframe.pro2
-rw-r--r--tests/widgets/qwebengineframe/resources/image.pngbin0 -> 14743 bytes
-rw-r--r--tests/widgets/qwebengineframe/resources/style.css1
-rw-r--r--tests/widgets/qwebengineframe/resources/test1.html1
-rw-r--r--tests/widgets/qwebengineframe/resources/test2.html1
-rw-r--r--tests/widgets/qwebengineframe/resources/testiframe.html53
-rw-r--r--tests/widgets/qwebengineframe/resources/testiframe2.html20
-rw-r--r--tests/widgets/qwebengineframe/tst_qwebengineframe.cpp1569
-rw-r--r--tests/widgets/qwebengineframe/tst_qwebengineframe.qrc10
-rw-r--r--tests/widgets/qwebenginehistory/qwebenginehistory.pro2
-rw-r--r--tests/widgets/qwebenginehistory/resources/page1.html1
-rw-r--r--tests/widgets/qwebenginehistory/resources/page2.html1
-rw-r--r--tests/widgets/qwebenginehistory/resources/page3.html1
-rw-r--r--tests/widgets/qwebenginehistory/resources/page4.html1
-rw-r--r--tests/widgets/qwebenginehistory/resources/page5.html1
-rw-r--r--tests/widgets/qwebenginehistory/resources/page6.html1
-rw-r--r--tests/widgets/qwebenginehistory/tst_qwebenginehistory.cpp528
-rw-r--r--tests/widgets/qwebenginehistory/tst_qwebenginehistory.qrc10
-rw-r--r--tests/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro2
-rw-r--r--tests/widgets/qwebenginehistoryinterface/tst_qwebenginehistoryinterface.cpp96
-rw-r--r--tests/widgets/qwebengineinspector/qwebengineinspector.pro2
-rw-r--r--tests/widgets/qwebengineinspector/tst_qwebengineinspector.cpp75
-rw-r--r--tests/widgets/qwebenginepage/qwebenginepage.pro3
-rw-r--r--tests/widgets/qwebenginepage/resources/content.html5
-rw-r--r--tests/widgets/qwebenginepage/resources/frame_a.html2
-rw-r--r--tests/widgets/qwebenginepage/resources/frame_c.html1
-rw-r--r--tests/widgets/qwebenginepage/resources/framedindex.html6
-rw-r--r--tests/widgets/qwebenginepage/resources/iframe.html6
-rw-r--r--tests/widgets/qwebenginepage/resources/iframe2.html6
-rw-r--r--tests/widgets/qwebenginepage/resources/iframe3.html5
-rw-r--r--tests/widgets/qwebenginepage/resources/index.html6
-rw-r--r--tests/widgets/qwebenginepage/resources/script.html3
-rw-r--r--tests/widgets/qwebenginepage/resources/user.css3
-rw-r--r--tests/widgets/qwebenginepage/tst_qwebenginepage.cpp3393
-rw-r--r--tests/widgets/qwebenginepage/tst_qwebenginepage.qrc14
-rw-r--r--tests/widgets/qwebengineview/qwebengineview.pro2
-rw-r--r--tests/widgets/qwebengineview/resources/frame_a.html2
-rw-r--r--tests/widgets/qwebengineview/resources/index.html4
-rw-r--r--tests/widgets/qwebengineview/resources/input_types.html9
-rw-r--r--tests/widgets/qwebengineview/resources/scrolltest_page.html6
-rw-r--r--tests/widgets/qwebengineview/tst_qwebengineview.cpp522
-rw-r--r--tests/widgets/qwebengineview/tst_qwebengineview.qrc8
-rw-r--r--tests/widgets/resources/image2.pngbin0 -> 14743 bytes
-rw-r--r--tests/widgets/resources/test.swfbin0 -> 10085 bytes
-rw-r--r--tests/widgets/tests.pri22
-rw-r--r--tests/widgets/util.h81
46 files changed, 6487 insertions, 0 deletions
diff --git a/tests/widgets/qwebengineframe/qwebengineframe.pro b/tests/widgets/qwebengineframe/qwebengineframe.pro
new file mode 100644
index 000000000..ff6c49628
--- /dev/null
+++ b/tests/widgets/qwebengineframe/qwebengineframe.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
diff --git a/tests/widgets/qwebengineframe/resources/image.png b/tests/widgets/qwebengineframe/resources/image.png
new file mode 100644
index 000000000..8d703640c
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/image.png
Binary files differ
diff --git a/tests/widgets/qwebengineframe/resources/style.css b/tests/widgets/qwebengineframe/resources/style.css
new file mode 100644
index 000000000..c05b747f1
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/style.css
@@ -0,0 +1 @@
+#idP {color: red !important}
diff --git a/tests/widgets/qwebengineframe/resources/test1.html b/tests/widgets/qwebengineframe/resources/test1.html
new file mode 100644
index 000000000..b323f966e
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/test1.html
@@ -0,0 +1 @@
+<html><body><p>Some text 1</p></body></html>
diff --git a/tests/widgets/qwebengineframe/resources/test2.html b/tests/widgets/qwebengineframe/resources/test2.html
new file mode 100644
index 000000000..63ac1f6ec
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/test2.html
@@ -0,0 +1 @@
+<html><body> <p>Some text 2</p></body></html>
diff --git a/tests/widgets/qwebengineframe/resources/testiframe.html b/tests/widgets/qwebengineframe/resources/testiframe.html
new file mode 100644
index 000000000..4b0e30ca5
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/testiframe.html
@@ -0,0 +1,53 @@
+<html>
+<head>
+<title></title>
+<style type="text/css">
+<!--
+#header {
+ background: #0f0;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 800px;
+ height: 100px;
+}
+#content1 {
+ background: #ff0;
+ position: absolute;
+ top: 101px;
+ left: 0px;
+ width: 400px;
+ height: 400px;
+ overflow: scroll;
+}
+#content2 {
+ background: #ff7;
+ position: absolute;
+ top: 101px;
+ left: 401px;
+ width: 400px;
+ height: 400px;
+}
+#footer {
+ background: #0f0;
+ position: absolute;
+ top: 502px;
+ left: 0px;
+ width: 800px;
+ height: 200px;
+}
+-->
+</style>
+</head>
+<body>
+<div id="header"></div>
+<div id="content1">You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.</div>
+<iframe id="content2" name="control" src="testiframe2.html"> </iframe>
+<div id="footer"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/tests/widgets/qwebengineframe/resources/testiframe2.html b/tests/widgets/qwebengineframe/resources/testiframe2.html
new file mode 100644
index 000000000..8957a5d8a
--- /dev/null
+++ b/tests/widgets/qwebengineframe/resources/testiframe2.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title></title>
+<style type="text/css">
+<!--
+#content {
+ background: #fff;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 800px;
+ height: 800px;
+}
+-->
+</style>
+</head>
+<body>
+<div id="content"> </div>
+</body>
+</html> \ No newline at end of file
diff --git a/tests/widgets/qwebengineframe/tst_qwebengineframe.cpp b/tests/widgets/qwebengineframe/tst_qwebengineframe.cpp
new file mode 100644
index 000000000..59f620b1b
--- /dev/null
+++ b/tests/widgets/qwebengineframe/tst_qwebengineframe.cpp
@@ -0,0 +1,1569 @@
+/*
+ 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 <qwebpage.h>
+#include <qwebelement.h>
+#include <qwebview.h>
+#include <qwebframe.h>
+#include <qwebhistory.h>
+#include <QAbstractItemView>
+#include <QApplication>
+#include <QComboBox>
+#include <QPaintEngine>
+#include <QPicture>
+#include <QRegExp>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QTextCodec>
+#ifndef QT_NO_OPENSSL
+#include <qsslerror.h>
+#endif
+#include "../util.h"
+
+class tst_QWebFrame : public QObject
+{
+ Q_OBJECT
+
+public:
+ bool eventFilter(QObject* watched, QEvent* event);
+
+public Q_SLOTS:
+ 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 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 setUrlWithFragment_data();
+ void setUrlWithFragment();
+ void setUrlToEmpty();
+ void setUrlToInvalid();
+ void setUrlHistory();
+ void setUrlUsingStateObject();
+ void setUrlSameUrl();
+ void setUrlThenLoads_data();
+ void setUrlThenLoads();
+ void loadFinishedAfterNotFoundError();
+ void loadInSignalHandlers_data();
+ void loadInSignalHandlers();
+
+private:
+ QWebView* m_view;
+ QWebPage* m_page;
+ QWebView* m_inputFieldsTestView;
+ int m_inputFieldTestPaintCount;
+};
+
+bool tst_QWebFrame::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_QWebFrame::init()
+{
+ m_view = new QWebView();
+ m_page = m_view->page();
+}
+
+void tst_QWebFrame::cleanup()
+{
+ delete m_view;
+}
+
+void tst_QWebFrame::symmetricUrl()
+{
+ QVERIFY(m_view->url().isEmpty());
+
+ QCOMPARE(m_view->history()->count(), 0);
+
+ QUrl dataUrl("data:text/html,<h1>Test");
+
+ m_view->setUrl(dataUrl);
+ QCOMPARE(m_view->url(), dataUrl);
+ QCOMPARE(m_view->history()->count(), 0);
+
+ // loading is _not_ immediate, so the text isn't set just yet.
+ QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty());
+
+ ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(m_view->history()->count(), 1);
+ QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test"));
+
+ QUrl dataUrl2("data:text/html,<h1>Test2");
+ QUrl dataUrl3("data:text/html,<h1>Test3");
+
+ m_view->setUrl(dataUrl2);
+ m_view->setUrl(dataUrl3);
+
+ QCOMPARE(m_view->url(), dataUrl3);
+
+ ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(m_view->history()->count(), 2);
+
+ QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3"));
+}
+
+void tst_QWebFrame::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);
+
+ // WebKit defines initialProgressValue as 10%, not 0%
+ QCOMPARE(progressSpy.first().first().toInt(), 10);
+
+ // But we always end at 100%
+ QCOMPARE(progressSpy.last().first().toInt(), 100);
+}
+
+void tst_QWebFrame::urlChange()
+{
+ QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
+
+ QUrl dataUrl("data:text/html,<h1>Test");
+ m_view->setUrl(dataUrl);
+
+ ::waitForSignal(m_page->mainFrame(), 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->mainFrame(), 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_QWebFrame::requestedUrl()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ // in few seconds, the image should be completely loaded
+ QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
+ FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
+ page.setNetworkAccessManager(networkManager);
+
+ frame->setUrl(QUrl("qrc:/test1.html"));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html"));
+ QCOMPARE(frame->url(), QUrl("qrc:/test2.html"));
+
+ frame->setUrl(QUrl("qrc:/non-existent.html"));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html"));
+ QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html"));
+
+ frame->setUrl(QUrl("http://abcdef.abcdef"));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(spy.count(), 3);
+ QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/"));
+ QCOMPARE(frame->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>)));
+ frame->setUrl(QUrl("qrc:/fake-ssl-error.html"));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(spy2.count(), 1);
+ QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html"));
+ QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html"));
+#endif
+}
+
+void tst_QWebFrame::requestedUrlAfterSetAndLoadFailures()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ QSignalSpy spy(frame, SIGNAL(loadFinished(bool)));
+
+ const QUrl first("http://abcdef.abcdef/");
+ frame->setUrl(first);
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QCOMPARE(frame->url(), first);
+ QCOMPARE(frame->requestedUrl(), first);
+ QVERIFY(!spy.at(0).first().toBool());
+
+ const QUrl second("http://abcdef.abcdef/another_page.html");
+ QVERIFY(first != second);
+
+ frame->load(second);
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QCOMPARE(frame->url(), first);
+ QCOMPARE(frame->requestedUrl(), second);
+ QVERIFY(!spy.at(1).first().toBool());
+}
+
+void tst_QWebFrame::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_QWebFrame::javaScriptWindowObjectCleared()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
+ QFETCH(QString, html);
+ frame->setHtml(html);
+
+ QFETCH(int, signalCount);
+ QCOMPARE(spy.count(), signalCount);
+}
+
+void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
+ frame->setHtml("<html></html>");
+ QCOMPARE(spy.count(), 0);
+ frame->evaluateJavaScript("var a = 'a';");
+ QCOMPARE(spy.count(), 1);
+ // no new clear for a new script:
+ frame->evaluateJavaScript("var a = 1;");
+ QCOMPARE(spy.count(), 1);
+}
+
+void tst_QWebFrame::setHtml()
+{
+ QString html("<html><head></head><body><p>hello world</p></body></html>");
+ QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool)));
+ m_view->page()->mainFrame()->setHtml(html);
+ QCOMPARE(m_view->page()->mainFrame()->toHtml(), html);
+ QCOMPARE(spy.count(), 1);
+}
+
+void tst_QWebFrame::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>");
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ frame->setHtml(html, QUrl(QLatin1String("file:///path/to/file")));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+
+ QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
+
+ // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources.
+
+ frame->setHtml(html);
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 0);
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 0);
+}
+
+void tst_QWebFrame::setHtmlWithStylesheetResource()
+{
+ // 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);
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ QWebElement webElement;
+
+ frame->setHtml(html, QUrl(QLatin1String("qrc:///file")));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ webElement = frame->documentElement().findFirst("p");
+ QCOMPARE(webElement.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red"));
+
+ // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources.
+
+ frame->setHtml(html, QUrl(QLatin1String("http://www.example.com/")));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ webElement = frame->documentElement().findFirst("p");
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118659", Continue);
+ QCOMPARE(webElement.styleProperty("color", QWebElement::CascadedStyle), QString());
+}
+
+void tst_QWebFrame::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>");
+
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ // in few seconds, the image should be completey loaded
+ QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
+
+ frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
+ waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(spy.count(), 1);
+
+ QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
+ QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
+
+ // no history item has to be added.
+ QCOMPARE(m_view->page()->history()->count(), 0);
+}
+
+class MyPage : public QWebPage
+{
+public:
+ MyPage() : QWebPage(), alerts(0) {}
+ int alerts;
+
+protected:
+ virtual void javaScriptAlert(QWebFrame*, const QString& msg)
+ {
+ alerts++;
+ QCOMPARE(msg, QString("foo"));
+ // Should not be enough to trigger deferred loading, since we've upped the HTML
+ // tokenizer delay in the Qt frameloader. See HTMLTokenizer::continueProcessing()
+ QTest::qWait(1000);
+ }
+};
+
+void tst_QWebFrame::setHtmlWithJSAlert()
+{
+ QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>");
+ MyPage page;
+ m_view->setPage(&page);
+ page.mainFrame()->setHtml(html);
+ QCOMPARE(page.alerts, 1);
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118663", Continue);
+ QCOMPARE(m_view->page()->mainFrame()->toHtml(), 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_QWebFrame::ipv6HostEncoding()
+{
+ 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()->mainFrame()->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"));
+}
+
+void tst_QWebFrame::metaData()
+{
+ 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()->mainFrame()->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()->mainFrame()->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());
+}
+
+#if !defined(QT_NO_COMBOBOX)
+void tst_QWebFrame::popupFocus()
+{
+ QWebView 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 QWebElement webCombo = view.page()->mainFrame()->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
+
+void tst_QWebFrame::inputFieldFocus()
+{
+ QWebView 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 QWebElement inputElement = view.page()->mainFrame()->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);
+}
+
+void tst_QWebFrame::hitTestContent()
+{
+ QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>");
+
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ frame->setHtml(html);
+ page.setViewportSize(QSize(200, 0)); //no height so link is not visible
+ const QWebElement linkElement = frame->documentElement().findFirst(QLatin1String("a#link"));
+ QWebHitTestResult result = frame->hitTestContent(linkElement.geometry().center());
+ QCOMPARE(result.linkText(), QString("link text"));
+ QWebElement link = result.linkElement();
+ QCOMPARE(link.attribute("target"), QString("_foo"));
+}
+
+void tst_QWebFrame::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_QWebFrame::baseUrl()
+{
+ QFETCH(QString, html);
+ QFETCH(QUrl, loadUrl);
+ QFETCH(QUrl, url);
+ QFETCH(QUrl, baseUrl);
+
+ m_page->mainFrame()->setHtml(html, loadUrl);
+ QCOMPARE(m_page->mainFrame()->url(), url);
+ QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl);
+}
+
+void tst_QWebFrame::hasSetFocus()
+{
+ QString html("<html><body><p>top</p>" \
+ "<iframe width='80%' height='30%'/>" \
+ "</body></html>");
+
+ QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
+ m_page->mainFrame()->setHtml(html);
+
+ waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200);
+ QCOMPARE(loadSpy.size(), 1);
+
+ QList<QWebFrame*> children = m_page->mainFrame()->childFrames();
+ QWebFrame* 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->mainFrame()->setFocus();
+ QTRY_VERIFY(m_page->mainFrame()->hasFocus());
+
+ for (int i = 0; i < children.size(); ++i) {
+ children.at(i)->setFocus();
+ QTRY_VERIFY(children.at(i)->hasFocus());
+ QVERIFY(!m_page->mainFrame()->hasFocus());
+ }
+
+ m_page->mainFrame()->setFocus();
+ QTRY_VERIFY(m_page->mainFrame()->hasFocus());
+}
+
+void tst_QWebFrame::renderGeometry()
+{
+ QString html("<html>" \
+ "<head><style>" \
+ "body, iframe { margin: 0px; border: none; }" \
+ "</style></head>" \
+ "<body><iframe width='100px' height='100px'/></body>" \
+ "</html>");
+
+ QWebPage page;
+ page.mainFrame()->setHtml(html);
+
+ QList<QWebFrame*> frames = page.mainFrame()->childFrames();
+ QWebFrame *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.mainFrame()->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, QWebFrame::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, QWebFrame::AllLayers);
+ painter2.end();
+
+ QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px
+ QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px
+}
+
+
+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_QWebFrame::renderHints()
+{
+ QString html("<html><body><p>Hello, world!</p></body></html>");
+
+ QWebPage page;
+ page.mainFrame()->setHtml(html);
+ page.setViewportSize(page.mainFrame()->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.mainFrame()->render(&painter);
+ QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing));
+ QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform));
+ QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing));
+
+ painter.setRenderHint(QPainter::TextAntialiasing, true);
+ page.mainFrame()->render(&painter);
+ QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform));
+ QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing));
+
+ painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
+ page.mainFrame()->render(&painter);
+ QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing));
+
+ painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
+ page.mainFrame()->render(&painter);
+ QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing);
+}
+
+void tst_QWebFrame::scrollPosition()
+{
+ // 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>");
+
+ QWebPage page;
+ page.setViewportSize(QSize(200, 200));
+
+ QWebFrame* frame = page.mainFrame();
+ frame->setHtml(html);
+ frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
+ frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
+
+ // try to set the scroll offset programmatically
+ frame->setScrollPosition(QPoint(23, 29));
+ QCOMPARE(frame->scrollPosition().x(), 23);
+ QCOMPARE(frame->scrollPosition().y(), 29);
+
+ int x = frame->evaluateJavaScript("window.scrollX").toInt();
+ int y = frame->evaluateJavaScript("window.scrollY").toInt();
+ QCOMPARE(x, 23);
+ QCOMPARE(y, 29);
+}
+
+void tst_QWebFrame::scrollToAnchor()
+{
+ QWebPage page;
+ page.setViewportSize(QSize(480, 800));
+ QWebFrame* frame = page.mainFrame();
+
+ 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>");
+ frame->setHtml(html);
+ frame->setScrollPosition(QPoint(0, 0));
+ QCOMPARE(frame->scrollPosition().x(), 0);
+ QCOMPARE(frame->scrollPosition().y(), 0);
+
+ QWebElement fooAnchor = frame->findFirstElement("a[id=foo]");
+
+ frame->scrollToAnchor("foo");
+ QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top());
+
+ frame->scrollToAnchor("bar");
+ frame->scrollToAnchor("foo");
+ QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top());
+
+ frame->scrollToAnchor("top");
+ QCOMPARE(frame->scrollPosition().y(), 0);
+
+ frame->scrollToAnchor("bar");
+ frame->scrollToAnchor("notexist");
+ QVERIFY(frame->scrollPosition().y() != 0);
+}
+
+
+void tst_QWebFrame::scrollbarsOff()
+{
+ QWebView view;
+ QWebFrame* mainFrame = view.page()->mainFrame();
+
+ 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"));
+}
+
+void tst_QWebFrame::horizontalScrollAfterBack()
+{
+ QWebView view;
+ QWebFrame* frame = view.page()->mainFrame();
+ QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool)));
+
+ view.page()->settings()->setMaximumPagesInCache(2);
+ frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded);
+ frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded);
+
+ view.load(QUrl("qrc:/testiframe2.html"));
+ view.resize(200, 200);
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height());
+
+ view.load(QUrl("qrc:/testiframe.html"));
+ QTRY_COMPARE(loadSpy.count(), 2);
+
+ view.page()->triggerAction(QWebPage::Back);
+ QTRY_COMPARE(loadSpy.count(), 3);
+ QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height());
+}
+
+void tst_QWebFrame::evaluateWillCauseRepaint()
+{
+ QWebView 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()->mainFrame()->evaluateJavaScript(
+ "document.getElementById('junk').style.display = 'none';");
+
+ ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect)));
+}
+
+void tst_QWebFrame::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_QWebFrame::setContent()
+{
+ QFETCH(QString, mimeType);
+ QFETCH(QByteArray, testContents);
+ QFETCH(QString, expected);
+ m_view->setContent(testContents, mimeType);
+ QWebFrame* mainFrame = m_view->page()->mainFrame();
+ QCOMPARE(expected , mainFrame->toPlainText());
+}
+
+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_QWebFrame::setCacheLoadControlAttribute()
+{
+ QWebPage page;
+ CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page);
+ page.setNetworkAccessManager(manager);
+ QWebFrame* frame = page.mainFrame();
+
+ QNetworkRequest request(QUrl("http://abcdef.abcdef/"));
+
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
+ frame->load(request);
+ QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache);
+
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
+ frame->load(request);
+ QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache);
+
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
+ frame->load(request);
+ QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork);
+
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
+ frame->load(request);
+ QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork);
+}
+
+void tst_QWebFrame::setUrlWithPendingLoads()
+{
+ QWebPage page;
+ page.mainFrame()->setHtml("<img src='dummy:'/>");
+ page.mainFrame()->setUrl(QUrl("about:blank"));
+}
+
+void tst_QWebFrame::setUrlWithFragment_data()
+{
+ QTest::addColumn<QUrl>("previousUrl");
+ QTest::newRow("empty") << QUrl();
+ QTest::newRow("same URL no fragment") << QUrl("qrc:/test1.html");
+ // See comments in setUrlSameUrl about using setUrl() with the same url().
+ QTest::newRow("same URL with same fragment") << QUrl("qrc:/test1.html#");
+ QTest::newRow("same URL with different fragment") << QUrl("qrc:/test1.html#anotherFragment");
+ QTest::newRow("another URL") << QUrl("qrc:/test2.html");
+}
+
+// Based on bug report https://bugs.webkit.org/show_bug.cgi?id=32723
+void tst_QWebFrame::setUrlWithFragment()
+{
+ QFETCH(QUrl, previousUrl);
+
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ if (!previousUrl.isEmpty()) {
+ frame->load(previousUrl);
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QCOMPARE(frame->url(), previousUrl);
+ }
+
+ QSignalSpy spy(frame, SIGNAL(loadFinished(bool)));
+ const QUrl url("qrc:/test1.html#");
+ QVERIFY(!url.fragment().isNull());
+
+ frame->setUrl(url);
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(spy.count(), 1);
+ QVERIFY(!frame->toPlainText().isEmpty());
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(frame->url(), url);
+}
+
+void tst_QWebFrame::setUrlToEmpty()
+{
+ int expectedLoadFinishedCount = 0;
+ const QUrl aboutBlank("about:blank");
+ const QUrl url("qrc:/test2.html");
+
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ QCOMPARE(frame->url(), QUrl());
+ QCOMPARE(frame->requestedUrl(), QUrl());
+ QCOMPARE(frame->baseUrl(), QUrl());
+
+ QSignalSpy spy(frame, SIGNAL(loadFinished(bool)));
+
+ // Set existing url
+ frame->setUrl(url);
+ expectedLoadFinishedCount++;
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(frame->baseUrl(), url);
+
+ // Set empty url
+ frame->setUrl(QUrl());
+ expectedLoadFinishedCount++;
+
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), aboutBlank);
+ QCOMPARE(frame->requestedUrl(), QUrl());
+ QCOMPARE(frame->baseUrl(), aboutBlank);
+
+ // Set existing url
+ frame->setUrl(url);
+ expectedLoadFinishedCount++;
+ ::waitForSignal(frame, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(frame->baseUrl(), url);
+
+ // Load empty url
+ frame->load(QUrl());
+ expectedLoadFinishedCount++;
+
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), aboutBlank);
+ QCOMPARE(frame->requestedUrl(), QUrl());
+ QCOMPARE(frame->baseUrl(), aboutBlank);
+}
+
+void tst_QWebFrame::setUrlToInvalid()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ const QUrl invalidUrl("http:/example.com");
+ QVERIFY(!invalidUrl.isEmpty());
+ QVERIFY(invalidUrl != QUrl());
+
+ // QWebFrame will do its best to accept the URL, possible converting it to a valid equivalent URL.
+ const QUrl validUrl("http://example.com/");
+ frame->setUrl(invalidUrl);
+ QCOMPARE(frame->url(), validUrl);
+ QCOMPARE(frame->requestedUrl(), validUrl);
+ QCOMPARE(frame->baseUrl(), 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());
+
+ frame->setUrl(anotherInvalidUrl);
+ QCOMPARE(frame->url(), aboutBlank);
+ QCOMPARE(frame->requestedUrl().toEncoded(), anotherInvalidUrl.toEncoded());
+ QCOMPARE(frame->baseUrl(), aboutBlank);
+}
+
+void tst_QWebFrame::setUrlHistory()
+{
+ const QUrl aboutBlank("about:blank");
+ QUrl url;
+ int expectedLoadFinishedCount = 0;
+ QWebFrame* frame = m_page->mainFrame();
+ QSignalSpy spy(frame, SIGNAL(loadFinished(bool)));
+
+ QCOMPARE(m_page->history()->count(), 0);
+
+ frame->setUrl(QUrl());
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), aboutBlank);
+ QCOMPARE(frame->requestedUrl(), QUrl());
+ QCOMPARE(m_page->history()->count(), 0);
+
+ url = QUrl("http://non.existent/");
+ frame->setUrl(url);
+ ::waitForSignal(m_page, SIGNAL(loadFinished(bool)));
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(m_page->history()->count(), 0);
+
+ url = QUrl("qrc:/test1.html");
+ frame->setUrl(url);
+ ::waitForSignal(m_page, SIGNAL(loadFinished(bool)));
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(m_page->history()->count(), 1);
+
+ frame->setUrl(QUrl());
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), aboutBlank);
+ QCOMPARE(frame->requestedUrl(), QUrl());
+ QCOMPARE(m_page->history()->count(), 1);
+
+ // Loading same page as current in history, so history count doesn't change.
+ url = QUrl("qrc:/test1.html");
+ frame->setUrl(url);
+ ::waitForSignal(m_page, SIGNAL(loadFinished(bool)));
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(m_page->history()->count(), 1);
+
+ url = QUrl("qrc:/test2.html");
+ frame->setUrl(url);
+ ::waitForSignal(m_page, SIGNAL(loadFinished(bool)));
+ expectedLoadFinishedCount++;
+ QCOMPARE(spy.count(), expectedLoadFinishedCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(m_page->history()->count(), 2);
+}
+
+void tst_QWebFrame::setUrlUsingStateObject()
+{
+ const QUrl aboutBlank("about:blank");
+ QUrl url;
+ QWebFrame* frame = m_page->mainFrame();
+ QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl)));
+ int expectedUrlChangeCount = 0;
+
+ QCOMPARE(m_page->history()->count(), 0);
+
+ url = QUrl("qrc:/test1.html");
+ frame->setUrl(url);
+ waitForSignal(m_page, SIGNAL(loadFinished(bool)));
+ expectedUrlChangeCount++;
+ QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(m_page->history()->count(), 1);
+
+ frame->evaluateJavaScript("window.history.pushState(null,'push', 'navigate/to/here')");
+ expectedUrlChangeCount++;
+ QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QCOMPARE(frame->url(), QUrl("qrc:/navigate/to/here"));
+ QCOMPARE(m_page->history()->count(), 2);
+ QVERIFY(m_page->history()->canGoBack());
+
+ frame->evaluateJavaScript("window.history.replaceState(null,'replace', 'another/location')");
+ expectedUrlChangeCount++;
+ QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QCOMPARE(frame->url(), QUrl("qrc:/navigate/to/another/location"));
+ QCOMPARE(m_page->history()->count(), 2);
+ QVERIFY(!m_page->history()->canGoForward());
+ QVERIFY(m_page->history()->canGoBack());
+
+ frame->evaluateJavaScript("window.history.back()");
+ QTest::qWait(100);
+ expectedUrlChangeCount++;
+ QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QCOMPARE(frame->url(), QUrl("qrc:/test1.html"));
+ QVERIFY(m_page->history()->canGoForward());
+ QVERIFY(!m_page->history()->canGoBack());
+}
+
+void tst_QWebFrame::setUrlSameUrl()
+{
+ const QUrl url1("qrc:/test1.html");
+ const QUrl url2("qrc:/test2.html");
+
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+ FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
+ page.setNetworkAccessManager(networkManager);
+
+ QSignalSpy spy(frame, SIGNAL(loadFinished(bool)));
+
+ frame->setUrl(url1);
+ waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QVERIFY(frame->url() != url1); // Nota bene: our QNAM redirects url1 to url2
+ QCOMPARE(frame->url(), url2);
+ QCOMPARE(spy.count(), 1);
+
+ frame->setUrl(url1);
+ waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QVERIFY(frame->url() != url1);
+ QCOMPARE(frame->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.
+ frame->setUrl(url2);
+ waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QCOMPARE(frame->url(), url2);
+ QCOMPARE(spy.count(), 3);
+
+ frame->setUrl(url1);
+ waitForSignal(frame, SIGNAL(loadFinished(bool)));
+ QCOMPARE(frame->url(), url2);
+ QCOMPARE(spy.count(), 4);
+}
+
+static inline QUrl extractBaseUrl(const QUrl& url)
+{
+ return url.resolved(QUrl());
+}
+
+void tst_QWebFrame::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_QWebFrame::setUrlThenLoads()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QUrl, baseUrl);
+ QWebFrame* frame = m_page->mainFrame();
+ QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl)));
+ QSignalSpy startedSpy(frame, SIGNAL(loadStarted()));
+ QSignalSpy finishedSpy(frame, SIGNAL(loadFinished(bool)));
+
+ frame->setUrl(url);
+ QCOMPARE(startedSpy.count(), 1);
+ ::waitForSignal(frame, SIGNAL(urlChanged(QUrl)));
+ QCOMPARE(urlChangedSpy.count(), 1);
+ QVERIFY(finishedSpy.at(0).first().toBool());
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), url);
+ QCOMPARE(frame->baseUrl(), baseUrl);
+
+ const QUrl urlToLoad1("qrc:/test2.html");
+ const QUrl urlToLoad2("qrc:/test1.html");
+
+ // Just after first load. URL didn't changed yet.
+ frame->load(urlToLoad1);
+ QCOMPARE(startedSpy.count(), 2);
+ QCOMPARE(frame->url(), url);
+ QCOMPARE(frame->requestedUrl(), urlToLoad1);
+ QCOMPARE(frame->baseUrl(), baseUrl);
+
+ // After first URL changed.
+ ::waitForSignal(frame, SIGNAL(urlChanged(QUrl)));
+ QCOMPARE(urlChangedSpy.count(), 2);
+ QVERIFY(finishedSpy.at(1).first().toBool());
+ QCOMPARE(frame->url(), urlToLoad1);
+ QCOMPARE(frame->requestedUrl(), urlToLoad1);
+ QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1));
+
+ // Just after second load. URL didn't changed yet.
+ frame->load(urlToLoad2);
+ QCOMPARE(startedSpy.count(), 3);
+ QCOMPARE(frame->url(), urlToLoad1);
+ QCOMPARE(frame->requestedUrl(), urlToLoad2);
+ QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1));
+
+ // After second URL changed.
+ ::waitForSignal(frame, SIGNAL(urlChanged(QUrl)));
+ QCOMPARE(urlChangedSpy.count(), 3);
+ QVERIFY(finishedSpy.at(2).first().toBool());
+ QCOMPARE(frame->url(), urlToLoad2);
+ QCOMPARE(frame->requestedUrl(), urlToLoad2);
+ QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad2));
+}
+
+void tst_QWebFrame::loadFinishedAfterNotFoundError()
+{
+ QWebPage page;
+ QWebFrame* frame = page.mainFrame();
+
+ QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
+ FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
+ page.setNetworkAccessManager(networkManager);
+
+ frame->setUrl(FakeReply::urlFor404ErrorWithoutContents);
+ QTRY_COMPARE(spy.count(), 1);
+ const bool wasLoadOk = spy.at(0).at(0).toBool();
+ QVERIFY(!wasLoadOk);
+}
+
+class URLSetter : public QObject {
+ Q_OBJECT
+
+public:
+ enum Signal {
+ LoadStarted,
+ LoadFinished,
+ ProvisionalLoad
+ };
+
+ enum Type {
+ UseLoad,
+ UseSetUrl
+ };
+
+ URLSetter(QWebFrame*, Signal, Type, const QUrl&);
+
+public Q_SLOTS:
+ void execute();
+
+Q_SIGNALS:
+ void finished();
+
+private:
+ QWebFrame* m_frame;
+ QUrl m_url;
+ Type m_type;
+};
+
+Q_DECLARE_METATYPE(URLSetter::Signal)
+Q_DECLARE_METATYPE(URLSetter::Type)
+
+URLSetter::URLSetter(QWebFrame* frame, Signal signal, URLSetter::Type type, const QUrl& url)
+ : m_frame(frame), m_url(url), m_type(type)
+{
+ if (signal == LoadStarted)
+ connect(m_frame, SIGNAL(loadStarted()), SLOT(execute()));
+ else if (signal == LoadFinished)
+ connect(m_frame, SIGNAL(loadFinished(bool)), SLOT(execute()));
+ else
+ connect(m_frame, SIGNAL(provisionalLoad()), SLOT(execute()));
+}
+
+void URLSetter::execute()
+{
+ // We track only the first emission.
+ m_frame->disconnect(this);
+ if (m_type == URLSetter::UseLoad)
+ m_frame->load(m_url);
+ else
+ m_frame->setUrl(m_url);
+ connect(m_frame, SIGNAL(loadFinished(bool)), SIGNAL(finished()));
+}
+
+void tst_QWebFrame::loadInSignalHandlers_data()
+{
+ 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_QWebFrame::loadInSignalHandlers()
+{
+ QFETCH(URLSetter::Type, type);
+ QFETCH(URLSetter::Signal, signal);
+ QFETCH(QUrl, url);
+
+ QWebFrame* frame = m_page->mainFrame();
+ const QUrl urlForSetter("qrc:/test1.html");
+ URLSetter setter(frame, signal, type, urlForSetter);
+
+ frame->load(url);
+ waitForSignal(&setter, SIGNAL(finished()), 200);
+ QCOMPARE(frame->url(), urlForSetter);
+}
+
+QTEST_MAIN(tst_QWebFrame)
+#include "tst_qwebframe.moc"
diff --git a/tests/widgets/qwebengineframe/tst_qwebengineframe.qrc b/tests/widgets/qwebengineframe/tst_qwebengineframe.qrc
new file mode 100644
index 000000000..2a7d0b9c2
--- /dev/null
+++ b/tests/widgets/qwebengineframe/tst_qwebengineframe.qrc
@@ -0,0 +1,10 @@
+<!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/widgets/qwebenginehistory/qwebenginehistory.pro b/tests/widgets/qwebenginehistory/qwebenginehistory.pro
new file mode 100644
index 000000000..ff6c49628
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/qwebenginehistory.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
diff --git a/tests/widgets/qwebenginehistory/resources/page1.html b/tests/widgets/qwebenginehistory/resources/page1.html
new file mode 100644
index 000000000..82fa4aff1
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page1.html
@@ -0,0 +1 @@
+<title>page1</title><body><h1>page1</h1></body>
diff --git a/tests/widgets/qwebenginehistory/resources/page2.html b/tests/widgets/qwebenginehistory/resources/page2.html
new file mode 100644
index 000000000..5307bdcfd
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page2.html
@@ -0,0 +1 @@
+<title>page2</title><body><h1>page2</h1></body>
diff --git a/tests/widgets/qwebenginehistory/resources/page3.html b/tests/widgets/qwebenginehistory/resources/page3.html
new file mode 100644
index 000000000..4e5547c7e
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page3.html
@@ -0,0 +1 @@
+<title>page3</title><body><h1>page3</h1></body>
diff --git a/tests/widgets/qwebenginehistory/resources/page4.html b/tests/widgets/qwebenginehistory/resources/page4.html
new file mode 100644
index 000000000..3c57aeddc
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page4.html
@@ -0,0 +1 @@
+<title>page4</title><body><h1>page4</h1></body>
diff --git a/tests/widgets/qwebenginehistory/resources/page5.html b/tests/widgets/qwebenginehistory/resources/page5.html
new file mode 100644
index 000000000..859355279
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page5.html
@@ -0,0 +1 @@
+<title>page5</title><body><h1>page5</h1></body>
diff --git a/tests/widgets/qwebenginehistory/resources/page6.html b/tests/widgets/qwebenginehistory/resources/page6.html
new file mode 100644
index 000000000..c5bbc6f79
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/resources/page6.html
@@ -0,0 +1 @@
+<title>page6</title><body><h1>page6</h1></body>
diff --git a/tests/widgets/qwebenginehistory/tst_qwebenginehistory.cpp b/tests/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
new file mode 100644
index 000000000..43474ae66
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
@@ -0,0 +1,528 @@
+/*
+ Copyright (C) 2008 Holger Hans Peter Freyther
+
+ 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 <QAction>
+
+#include "../util.h"
+#include "qwebpage.h"
+#include "qwebview.h"
+#include "qwebframe.h"
+#include "qwebhistory.h"
+#include "qdebug.h"
+
+class tst_QWebHistory : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebHistory();
+ virtual ~tst_QWebHistory();
+
+protected :
+ void loadPage(int nr)
+ {
+ frame->load(QUrl("qrc:/resources/page" + QString::number(nr) + ".html"));
+ loadFinishedBarrier->ensureSignalEmitted();
+ }
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private Q_SLOTS:
+ void title();
+ void count();
+ void back();
+ void forward();
+ void itemAt();
+ void goToItem();
+ void items();
+ void serialize_1(); //QWebHistory countity
+ void serialize_2(); //QWebHistory index
+ void serialize_3(); //QWebHistoryItem
+ // Those tests shouldn't crash
+ void saveAndRestore_crash_1();
+ void saveAndRestore_crash_2();
+ void saveAndRestore_crash_3();
+ void saveAndRestore_crash_4();
+
+ void popPushState_data();
+ void popPushState();
+ void clear();
+ void restoreIncompatibleVersion1();
+
+
+private:
+ QWebPage* page;
+ QWebFrame* frame;
+ QWebHistory* hist;
+ QScopedPointer<SignalBarrier> loadFinishedBarrier;
+ int histsize;
+};
+
+tst_QWebHistory::tst_QWebHistory()
+{
+}
+
+tst_QWebHistory::~tst_QWebHistory()
+{
+}
+
+void tst_QWebHistory::init()
+{
+ page = new QWebPage(this);
+ frame = page->mainFrame();
+ loadFinishedBarrier.reset(new SignalBarrier(frame, SIGNAL(loadFinished(bool))));
+
+ for (int i = 1;i < 6;i++) {
+ loadPage(i);
+ }
+ hist = page->history();
+ histsize = 5;
+}
+
+void tst_QWebHistory::cleanup()
+{
+ loadFinishedBarrier.reset();
+ delete page;
+}
+
+/**
+ * Check QWebHistoryItem::title() method
+ */
+void tst_QWebHistory::title()
+{
+ QCOMPARE(hist->currentItem().title(), QString("page5"));
+}
+
+/**
+ * Check QWebHistory::count() method
+ */
+void tst_QWebHistory::count()
+{
+ QCOMPARE(hist->count(), histsize);
+}
+
+/**
+ * Check QWebHistory::back() method
+ */
+void tst_QWebHistory::back()
+{
+ for (int i = histsize;i > 1;i--) {
+ QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(i));
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ }
+ //try one more time (too many). crash test
+ hist->back();
+ QCOMPARE(page->mainFrame()->toPlainText(), QString("page1"));
+}
+
+/**
+ * Check QWebHistory::forward() method
+ */
+void tst_QWebHistory::forward()
+{
+ //rewind history :-)
+ while (hist->canGoBack()) {
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ }
+
+ for (int i = 1;i < histsize;i++) {
+ QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(i));
+ hist->forward();
+ loadFinishedBarrier->ensureSignalEmitted();
+ }
+ //try one more time (too many). crash test
+ hist->forward();
+ QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(histsize));
+}
+
+/**
+ * Check QWebHistory::itemAt() method
+ */
+void tst_QWebHistory::itemAt()
+{
+ for (int i = 1;i < histsize;i++) {
+ QCOMPARE(hist->itemAt(i - 1).title(), QString("page") + QString::number(i));
+ QVERIFY(hist->itemAt(i - 1).isValid());
+ }
+ //check out of range values
+ QVERIFY(!hist->itemAt(-1).isValid());
+ QVERIFY(!hist->itemAt(histsize).isValid());
+}
+
+/**
+ * Check QWebHistory::goToItem() method
+ */
+void tst_QWebHistory::goToItem()
+{
+ QWebHistoryItem current = hist->currentItem();
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ QVERIFY(hist->currentItem().title() != current.title());
+ hist->goToItem(current);
+ loadFinishedBarrier->ensureSignalEmitted();
+ QCOMPARE(hist->currentItem().title(), current.title());
+}
+
+/**
+ * Check QWebHistory::items() method
+ */
+void tst_QWebHistory::items()
+{
+ QList<QWebHistoryItem> items = hist->items();
+ //check count
+ QCOMPARE(histsize, items.count());
+
+ //check order
+ for (int i = 1;i <= histsize;i++) {
+ QCOMPARE(items.at(i - 1).title(), QString("page") + QString::number(i));
+ }
+}
+
+/**
+ * Check history state after serialization (pickle, persistent..) method
+ * Checks history size, history order
+ */
+void tst_QWebHistory::serialize_1()
+{
+ QByteArray tmp; //buffer
+ QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved
+ QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded
+
+ save << *hist;
+ QVERIFY(save.status() == QDataStream::Ok);
+ QCOMPARE(hist->count(), histsize);
+
+ //check size of history
+ //load next page to find differences
+ loadPage(6);
+ QCOMPARE(hist->count(), histsize + 1);
+ load >> *hist;
+ QVERIFY(load.status() == QDataStream::Ok);
+ QCOMPARE(hist->count(), histsize);
+
+ //check order of historyItems
+ QList<QWebHistoryItem> items = hist->items();
+ for (int i = 1;i <= histsize;i++) {
+ QCOMPARE(items.at(i - 1).title(), QString("page") + QString::number(i));
+ }
+}
+
+/**
+ * Check history state after serialization (pickle, persistent..) method
+ * Checks history currentIndex value
+ */
+void tst_QWebHistory::serialize_2()
+{
+ QByteArray tmp; //buffer
+ QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved
+ QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded
+
+ // Force a "same document" navigation.
+ frame->load(frame->url().toString() + QLatin1String("#dummyAnchor"));
+
+ int initialCurrentIndex = hist->currentItemIndex();
+
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ hist->back();
+ loadFinishedBarrier->ensureSignalEmitted();
+ //check if current index was changed (make sure that it is not last item)
+ QVERIFY(hist->currentItemIndex() != initialCurrentIndex);
+ //save current index
+ int oldCurrentIndex = hist->currentItemIndex();
+
+ save << *hist;
+ QVERIFY(save.status() == QDataStream::Ok);
+ load >> *hist;
+ QVERIFY(load.status() == QDataStream::Ok);
+
+ //check current index
+ QCOMPARE(hist->currentItemIndex(), oldCurrentIndex);
+
+ hist->forward();
+ loadFinishedBarrier->ensureSignalEmitted();
+ hist->forward();
+ loadFinishedBarrier->ensureSignalEmitted();
+ hist->forward();
+ loadFinishedBarrier->ensureSignalEmitted();
+ QCOMPARE(hist->currentItemIndex(), initialCurrentIndex);
+}
+
+/**
+ * Check history state after serialization (pickle, persistent..) method
+ * Checks QWebHistoryItem public property after serialization
+ */
+void tst_QWebHistory::serialize_3()
+{
+ QByteArray tmp; //buffer
+ QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved
+ QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded
+
+ //prepare two different history items
+ QWebHistoryItem a = hist->currentItem();
+ a.setUserData("A - user data");
+
+ //check properties BEFORE serialization
+ QString title(a.title());
+ QDateTime lastVisited(a.lastVisited());
+ QUrl originalUrl(a.originalUrl());
+ QUrl url(a.url());
+ QVariant userData(a.userData());
+
+ save << *hist;
+ QVERIFY(save.status() == QDataStream::Ok);
+ QVERIFY(!load.atEnd());
+ hist->clear();
+ QVERIFY(hist->count() == 1);
+ load >> *hist;
+ QVERIFY(load.status() == QDataStream::Ok);
+ QWebHistoryItem b = hist->currentItem();
+
+ //check properties AFTER serialization
+ QCOMPARE(b.title(), title);
+ QCOMPARE(b.lastVisited(), lastVisited);
+ QCOMPARE(b.originalUrl(), originalUrl);
+ QCOMPARE(b.url(), url);
+ QCOMPARE(b.userData(), userData);
+
+ //Check if all data was read
+ QVERIFY(load.atEnd());
+}
+
+static void saveHistory(QWebHistory* history, QByteArray* in)
+{
+ in->clear();
+ QDataStream save(in, QIODevice::WriteOnly);
+ save << *history;
+}
+
+static void restoreHistory(QWebHistory* history, QByteArray* out)
+{
+ QDataStream load(out, QIODevice::ReadOnly);
+ load >> *history;
+}
+
+void tst_QWebHistory::saveAndRestore_crash_1()
+{
+ QByteArray buffer;
+ saveHistory(hist, &buffer);
+ for (unsigned i = 0; i < 5; i++) {
+ restoreHistory(hist, &buffer);
+ saveHistory(hist, &buffer);
+ }
+}
+
+void tst_QWebHistory::saveAndRestore_crash_2()
+{
+ QByteArray buffer;
+ saveHistory(hist, &buffer);
+ QWebPage* page2 = new QWebPage(this);
+ QWebHistory* hist2 = page2->history();
+ for (unsigned i = 0; i < 5; i++) {
+ restoreHistory(hist2, &buffer);
+ saveHistory(hist2, &buffer);
+ }
+ delete page2;
+}
+
+void tst_QWebHistory::saveAndRestore_crash_3()
+{
+ QByteArray buffer;
+ saveHistory(hist, &buffer);
+ QWebPage* page2 = new QWebPage(this);
+ QWebHistory* hist1 = hist;
+ QWebHistory* hist2 = page2->history();
+ for (unsigned i = 0; i < 5; i++) {
+ restoreHistory(hist1, &buffer);
+ restoreHistory(hist2, &buffer);
+ QVERIFY(hist1->count() == hist2->count());
+ QVERIFY(hist1->count() == histsize);
+ hist2->back();
+ saveHistory(hist2, &buffer);
+ hist2->clear();
+ }
+ delete page2;
+}
+
+void tst_QWebHistory::saveAndRestore_crash_4()
+{
+ QByteArray buffer;
+ saveHistory(hist, &buffer);
+
+ QWebPage* page2 = new QWebPage(this);
+ // The initial crash was in PageCache.
+ page2->settings()->setMaximumPagesInCache(3);
+
+ // Load the history in a new page, waiting for the load to finish.
+ QEventLoop waitForLoadFinished;
+ QObject::connect(page2, SIGNAL(loadFinished(bool)), &waitForLoadFinished, SLOT(quit()), Qt::QueuedConnection);
+ QDataStream load(&buffer, QIODevice::ReadOnly);
+ load >> *page2->history();
+ waitForLoadFinished.exec();
+
+ delete page2;
+ // Give some time for the PageCache cleanup 0-timer to fire.
+ QTest::qWait(50);
+}
+
+void tst_QWebHistory::popPushState_data()
+{
+ QTest::addColumn<QString>("script");
+ QTest::newRow("pushState") << "history.pushState(123, \"foo\");";
+ QTest::newRow("replaceState") << "history.replaceState(\"a\", \"b\");";
+ QTest::newRow("back") << "history.back();";
+ QTest::newRow("forward") << "history.forward();";
+ QTest::newRow("clearState") << "history.clearState();";
+}
+
+/** Crash test, WebKit bug 38840 (https://bugs.webkit.org/show_bug.cgi?id=38840) */
+void tst_QWebHistory::popPushState()
+{
+ QFETCH(QString, script);
+ QWebPage page;
+ page.mainFrame()->setHtml("<html><body>long live Qt!</body></html>");
+ page.mainFrame()->evaluateJavaScript(script);
+}
+
+/** ::clear */
+void tst_QWebHistory::clear()
+{
+ QByteArray buffer;
+
+ QAction* actionBack = page->action(QWebPage::Back);
+ QVERIFY(actionBack->isEnabled());
+ saveHistory(hist, &buffer);
+ QVERIFY(hist->count() > 1);
+ hist->clear();
+ QVERIFY(hist->count() == 1); // Leave current item.
+ QVERIFY(!actionBack->isEnabled());
+
+ QWebPage* page2 = new QWebPage(this);
+ QWebHistory* hist2 = page2->history();
+ QVERIFY(hist2->count() == 0);
+ hist2->clear();
+ QVERIFY(hist2->count() == 0); // Do not change anything.
+ delete page2;
+}
+
+// static void dumpCurrentVersion(QWebHistory* history)
+// {
+// QByteArray buffer;
+// saveHistory(history, &buffer);
+// printf(" static const char version1Dump[] = {");
+// for (int i = 0; i < buffer.size(); ++i) {
+// bool newLine = !(i % 15);
+// bool last = i == buffer.size() - 1;
+// printf("%s0x%.2x%s", newLine ? "\n " : "", (unsigned char)buffer[i], last ? "" : ", ");
+// }
+// printf("};\n");
+// }
+
+void tst_QWebHistory::restoreIncompatibleVersion1()
+{
+ // Uncomment this code to generate a dump similar to the one below with the current stream version.
+ // dumpCurrentVersion(hist);
+ static const unsigned char version1Dump[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x65,
+ 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x68,
+ 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x61, 0x00,
+ 0x67, 0x00, 0x65, 0x00, 0x31, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00,
+ 0x2f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x31, 0x00, 0x2e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32,
+ 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f,
+ 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x68, 0x00,
+ 0x74, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67,
+ 0x00, 0x65, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x32,
+ 0x00, 0x2e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00,
+ 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73,
+ 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00,
+ 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x68, 0x00, 0x74,
+ 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00,
+ 0x65, 0x00, 0x33, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00,
+ 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65,
+ 0x00, 0x73, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x33, 0x00,
+ 0x2e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+ 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x71,
+ 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x70,
+ 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x68, 0x00, 0x74, 0x00,
+ 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65,
+ 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72,
+ 0x00, 0x65, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x34, 0x00, 0x2e,
+ 0x00, 0x68, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x71, 0x00,
+ 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6f,
+ 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x70, 0x00,
+ 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6d,
+ 0x00, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x35, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x32, 0x00, 0x71, 0x00, 0x72, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x72, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73,
+ 0x00, 0x2f, 0x00, 0x70, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x35, 0x00, 0x2e, 0x00,
+ 0x68, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ QByteArray version1(reinterpret_cast<const char*>(version1Dump), sizeof(version1Dump));
+ QDataStream stream(&version1, QIODevice::ReadOnly);
+
+ // This should fail to load, the history should be cleared and the stream should be broken.
+ stream >> *hist;
+ QVERIFY(!hist->canGoBack());
+ QVERIFY(!hist->canGoForward());
+ QVERIFY(stream.status() == QDataStream::ReadCorruptData);
+}
+
+QTEST_MAIN(tst_QWebHistory)
+#include "tst_qwebhistory.moc"
diff --git a/tests/widgets/qwebenginehistory/tst_qwebenginehistory.qrc b/tests/widgets/qwebenginehistory/tst_qwebenginehistory.qrc
new file mode 100644
index 000000000..cdfe575a0
--- /dev/null
+++ b/tests/widgets/qwebenginehistory/tst_qwebenginehistory.qrc
@@ -0,0 +1,10 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resources/page1.html</file>
+ <file>resources/page2.html</file>
+ <file>resources/page3.html</file>
+ <file>resources/page4.html</file>
+ <file>resources/page5.html</file>
+ <file>resources/page6.html</file>
+</qresource>
+</RCC>
diff --git a/tests/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro b/tests/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro
new file mode 100644
index 000000000..ff6c49628
--- /dev/null
+++ b/tests/widgets/qwebenginehistoryinterface/qwebenginehistoryinterface.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
diff --git a/tests/widgets/qwebenginehistoryinterface/tst_qwebenginehistoryinterface.cpp b/tests/widgets/qwebenginehistoryinterface/tst_qwebenginehistoryinterface.cpp
new file mode 100644
index 000000000..f4571cf74
--- /dev/null
+++ b/tests/widgets/qwebenginehistoryinterface/tst_qwebenginehistoryinterface.cpp
@@ -0,0 +1,96 @@
+/*
+ Copyright (C) 2008 Holger Hans Peter Freyther
+
+ 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 <qwebpage.h>
+#include <qwebview.h>
+#include <qwebframe.h>
+#include <qwebelement.h>
+#include <qwebhistoryinterface.h>
+#include <QDebug>
+
+class tst_QWebHistoryInterface : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebHistoryInterface();
+ virtual ~tst_QWebHistoryInterface();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private Q_SLOTS:
+ void visitedLinks();
+
+private:
+
+
+private:
+ QWebView* m_view;
+ QWebPage* m_page;
+};
+
+tst_QWebHistoryInterface::tst_QWebHistoryInterface()
+{
+}
+
+tst_QWebHistoryInterface::~tst_QWebHistoryInterface()
+{
+}
+
+void tst_QWebHistoryInterface::init()
+{
+ m_view = new QWebView();
+ m_page = m_view->page();
+}
+
+void tst_QWebHistoryInterface::cleanup()
+{
+ delete m_view;
+}
+
+class FakeHistoryImplementation : public QWebHistoryInterface {
+public:
+ void addHistoryEntry(const QString&) {}
+ bool historyContains(const QString& url) const {
+ return url == QLatin1String("http://www.trolltech.com/");
+ }
+};
+
+
+/*
+ * Test that visited links are properly colored. http://www.trolltech.com is marked
+ * as visited, so the below website should have exactly one element in the a:visited
+ * state.
+ */
+void tst_QWebHistoryInterface::visitedLinks()
+{
+ QWebHistoryInterface::setDefaultInterface(new FakeHistoryImplementation);
+ m_view->setHtml("<html><style>:link{color:green}:visited{color:red}</style><body><a href='http://www.trolltech.com' id='vlink'>Trolltech</a></body></html>");
+ QWebElement anchor = m_view->page()->mainFrame()->findFirstElement("a[id=vlink]");
+ QString linkColor = anchor.styleProperty("color", QWebElement::ComputedStyle);
+ QCOMPARE(linkColor, QString::fromLatin1("rgb(255, 0, 0)"));
+}
+
+QTEST_MAIN(tst_QWebHistoryInterface)
+#include "tst_qwebhistoryinterface.moc"
diff --git a/tests/widgets/qwebengineinspector/qwebengineinspector.pro b/tests/widgets/qwebengineinspector/qwebengineinspector.pro
new file mode 100644
index 000000000..ff6c49628
--- /dev/null
+++ b/tests/widgets/qwebengineinspector/qwebengineinspector.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
diff --git a/tests/widgets/qwebengineinspector/tst_qwebengineinspector.cpp b/tests/widgets/qwebengineinspector/tst_qwebengineinspector.cpp
new file mode 100644
index 000000000..37e62f67b
--- /dev/null
+++ b/tests/widgets/qwebengineinspector/tst_qwebengineinspector.cpp
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2008 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 <qdir.h>
+#include <qwebinspector.h>
+#include <qwebpage.h>
+#include <qwebsettings.h>
+
+class tst_QWebInspector : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void attachAndDestroyPageFirst();
+ void attachAndDestroyInspectorFirst();
+ void attachAndDestroyInternalInspector();
+};
+
+void tst_QWebInspector::attachAndDestroyPageFirst()
+{
+ // External inspector + manual destruction of page first
+ QWebPage* page = new QWebPage();
+ page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+ QWebInspector* inspector = new QWebInspector();
+ inspector->setPage(page);
+ page->updatePositionDependentActions(QPoint(0, 0));
+ page->triggerAction(QWebPage::InspectElement);
+
+ delete page;
+ delete inspector;
+}
+
+void tst_QWebInspector::attachAndDestroyInspectorFirst()
+{
+ // External inspector + manual destruction of inspector first
+ QWebPage* page = new QWebPage();
+ page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+ QWebInspector* inspector = new QWebInspector();
+ inspector->setPage(page);
+ page->updatePositionDependentActions(QPoint(0, 0));
+ page->triggerAction(QWebPage::InspectElement);
+
+ delete inspector;
+ delete page;
+}
+
+void tst_QWebInspector::attachAndDestroyInternalInspector()
+{
+ // Internal inspector
+ QWebPage page;
+ page.settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+ page.updatePositionDependentActions(QPoint(0, 0));
+ page.triggerAction(QWebPage::InspectElement);
+}
+
+QTEST_MAIN(tst_QWebInspector)
+
+#include "tst_qwebinspector.moc"
diff --git a/tests/widgets/qwebenginepage/qwebenginepage.pro b/tests/widgets/qwebenginepage/qwebenginepage.pro
new file mode 100644
index 000000000..e56bbe8f7
--- /dev/null
+++ b/tests/widgets/qwebenginepage/qwebenginepage.pro
@@ -0,0 +1,3 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
+QT *= core-private gui-private
diff --git a/tests/widgets/qwebenginepage/resources/content.html b/tests/widgets/qwebenginepage/resources/content.html
new file mode 100644
index 000000000..360ad65ef
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/content.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<a>This is test content</a>
+</body>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/frame_a.html b/tests/widgets/qwebenginepage/resources/frame_a.html
new file mode 100644
index 000000000..9ff68f13a
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/frame_a.html
@@ -0,0 +1,2 @@
+<a href="http://google.com" target="frame_b"><img src="" width=100 height=100 alt="Google"></a>
+<a href="http://yahoo.com" target="frame_b"><img src="" width=100 height=100 alt="Yahoo"></a>
diff --git a/tests/widgets/qwebenginepage/resources/frame_c.html b/tests/widgets/qwebenginepage/resources/frame_c.html
new file mode 100644
index 000000000..eba9ca0eb
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/frame_c.html
@@ -0,0 +1 @@
+<a href="content.html" target="frame_b"><img src="" width=100 height=100 alt="Google"></a>
diff --git a/tests/widgets/qwebenginepage/resources/framedindex.html b/tests/widgets/qwebenginepage/resources/framedindex.html
new file mode 100644
index 000000000..be4500483
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/framedindex.html
@@ -0,0 +1,6 @@
+<html>
+<frameset cols="25%,75%">
+ <frame src="frame_c.html" name="frame_c">
+ <frame src="frame_b.html" name="frame_b">
+</frameset>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/iframe.html b/tests/widgets/qwebenginepage/resources/iframe.html
new file mode 100644
index 000000000..f17027c7a
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/iframe.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<p>top</p>
+<iframe src="iframe2.html" width="80%" height="30%"/>
+</body>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/iframe2.html b/tests/widgets/qwebenginepage/resources/iframe2.html
new file mode 100644
index 000000000..758a44a2c
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/iframe2.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<p>another iframe</p>
+<iframe src="iframe3.html" width="80%" height="30%"></iframe>
+</body>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/iframe3.html b/tests/widgets/qwebenginepage/resources/iframe3.html
new file mode 100644
index 000000000..ed6ac5b94
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/iframe3.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<p>inner</p>
+</body>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/index.html b/tests/widgets/qwebenginepage/resources/index.html
new file mode 100644
index 000000000..638df364e
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/index.html
@@ -0,0 +1,6 @@
+<html>
+<frameset cols="25%,75%">
+ <frame src="frame_a.html" name="frame_a">
+ <frame src="frame_b.html" name="frame_b">
+</frameset>
+</html>
diff --git a/tests/widgets/qwebenginepage/resources/script.html b/tests/widgets/qwebenginepage/resources/script.html
new file mode 100644
index 000000000..ede986415
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/script.html
@@ -0,0 +1,3 @@
+<html><head>
+<script language="javascript" type="text/javascript" src="does_not_exist.js"></script>
+</head></html>
diff --git a/tests/widgets/qwebenginepage/resources/user.css b/tests/widgets/qwebenginepage/resources/user.css
new file mode 100644
index 000000000..4ccb2f0fc
--- /dev/null
+++ b/tests/widgets/qwebenginepage/resources/user.css
@@ -0,0 +1,3 @@
+p {
+ background-image: url('http://does.not/exist.png');
+} \ No newline at end of file
diff --git a/tests/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/widgets/qwebenginepage/tst_qwebenginepage.cpp
new file mode 100644
index 000000000..a29f6c0f3
--- /dev/null
+++ b/tests/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -0,0 +1,3393 @@
+/*
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
+ Copyright (C) 2010 Holger Hans Peter Freyther
+
+ 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 "../util.h"
+#include "../WebCoreSupport/DumpRenderTreeSupportQt.h"
+#include <QClipboard>
+#include <QDir>
+#include <QGraphicsWidget>
+#include <QLineEdit>
+#include <QMainWindow>
+#include <QMenu>
+#include <QMimeDatabase>
+#include <QPushButton>
+#include <QStateMachine>
+#include <QStyle>
+#include <QtTest/QtTest>
+#include <QTextCharFormat>
+#include <private/qinputmethod_p.h>
+#include <qgraphicsscene.h>
+#include <qgraphicsview.h>
+#include <qgraphicswebview.h>
+#include <qnetworkcookiejar.h>
+#include <qnetworkreply.h>
+#include <qnetworkrequest.h>
+#include <qpa/qplatforminputcontext.h>
+#include <qwebdatabase.h>
+#include <qwebelement.h>
+#include <qwebframe.h>
+#include <qwebhistory.h>
+#include <qwebpage.h>
+#include <qwebsecurityorigin.h>
+#include <qwebview.h>
+#include <qimagewriter.h>
+
+static void removeRecursive(const QString& dirname)
+{
+ QDir dir(dirname);
+ QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
+ for (int i = 0; i < entries.count(); ++i)
+ if (entries[i].isDir())
+ removeRecursive(entries[i].filePath());
+ else
+ dir.remove(entries[i].fileName());
+ QDir().rmdir(dirname);
+}
+
+class TestInputContext : public QPlatformInputContext
+{
+public:
+ TestInputContext()
+ : m_visible(false)
+ {
+ QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
+ inputMethodPrivate->testContext = this;
+ }
+
+ ~TestInputContext()
+ {
+ QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
+ inputMethodPrivate->testContext = 0;
+ }
+
+ virtual void showInputPanel()
+ {
+ m_visible = true;
+ }
+ virtual void hideInputPanel()
+ {
+ m_visible = false;
+ }
+ virtual bool isInputPanelVisible() const
+ {
+ return m_visible;
+ }
+
+ bool m_visible;
+};
+
+class tst_QWebPage : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebPage();
+ virtual ~tst_QWebPage();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+ void cleanupFiles();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void thirdPartyCookiePolicy();
+ void contextMenuCopy();
+ void contextMenuPopulatedOnce();
+ void acceptNavigationRequest();
+ void geolocationRequestJS();
+ void loadFinished();
+ void actionStates();
+ void popupFormSubmission();
+ void acceptNavigationRequestWithNewWindow();
+ void userStyleSheet();
+ void userStyleSheetFromLocalFileUrl();
+ void userStyleSheetFromQrcUrl();
+ void loadHtml5Video();
+ void modified();
+ void contextMenuCrash();
+ void updatePositionDependentActionsCrash();
+ void database();
+ void createPluginWithPluginsEnabled();
+ void createPluginWithPluginsDisabled();
+ void destroyPlugin_data();
+ void destroyPlugin();
+ void createViewlessPlugin_data();
+ void createViewlessPlugin();
+ void graphicsWidgetPlugin();
+ void multiplePageGroupsAndLocalStorage();
+ void cursorMovements();
+ void textSelection();
+ void textEditing();
+ void backActionUpdate();
+ void frameAt();
+ void requestCache();
+ void loadCachedPage();
+ void protectBindingsRuntimeObjectsFromCollector();
+ void localURLSchemes();
+ void testOptionalJSObjects();
+ void testLocalStorageVisibility();
+ void testEnablePersistentStorage();
+ void consoleOutput();
+ void inputMethods_data();
+ void inputMethods();
+ void inputMethodsTextFormat_data();
+ void inputMethodsTextFormat();
+ void defaultTextEncoding();
+ void errorPageExtension();
+ void errorPageExtensionInIFrames();
+ void errorPageExtensionInFrameset();
+ void errorPageExtensionLoadFinished();
+ void userAgentApplicationName();
+ void userAgentNewlineStripping();
+ void undoActionHaveCustomText();
+
+ void viewModes();
+
+ void crashTests_LazyInitializationOfMainFrame();
+
+ void screenshot_data();
+ void screenshot();
+
+#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
+ void acceleratedWebGLScreenshotWithoutView();
+ void unacceleratedWebGLScreenshotWithoutView();
+#endif
+
+ void originatingObjectInNetworkRequests();
+ void networkReplyParentDidntChange();
+ void destroyQNAMBeforeAbortDoesntCrash();
+ void testJSPrompt();
+ void showModalDialog();
+ void testStopScheduledPageRefresh();
+ void findText();
+ void supportedContentType();
+ // [Qt] tst_QWebPage::infiniteLoopJS() timeouts with DFG JIT
+ // https://bugs.webkit.org/show_bug.cgi?id=79040
+ // void infiniteLoopJS();
+ void navigatorCookieEnabled();
+ void deleteQWebViewTwice();
+ void renderOnRepaintRequestedShouldNotRecurse();
+ void loadSignalsOrder_data();
+ void loadSignalsOrder();
+ void openWindowDefaultSize();
+ void cssMediaTypeGlobalSetting();
+ void cssMediaTypePageSetting();
+
+#ifdef Q_OS_MAC
+ void macCopyUnicodeToClipboard();
+#endif
+
+private:
+ QWebView* m_view;
+ QWebPage* m_page;
+ QString tmpDirPath() const
+ {
+ static QString tmpd = QDir::tempPath() + "/tst_qwebpage-"
+ + QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddhhmmss"));
+ return tmpd;
+ }
+};
+
+tst_QWebPage::tst_QWebPage()
+{
+}
+
+tst_QWebPage::~tst_QWebPage()
+{
+}
+
+void tst_QWebPage::init()
+{
+ m_view = new QWebView();
+ m_page = m_view->page();
+}
+
+void tst_QWebPage::cleanup()
+{
+ delete m_view;
+}
+
+void tst_QWebPage::cleanupFiles()
+{
+ removeRecursive(tmpDirPath());
+}
+
+void tst_QWebPage::initTestCase()
+{
+ cleanupFiles(); // In case there are old files from previous runs
+}
+
+void tst_QWebPage::cleanupTestCase()
+{
+ cleanupFiles(); // Be nice
+}
+
+class NavigationRequestOverride : public QWebPage
+{
+public:
+ NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
+
+ bool m_acceptNavigationRequest;
+protected:
+ virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
+ Q_UNUSED(frame);
+ Q_UNUSED(request);
+ Q_UNUSED(type);
+
+ return m_acceptNavigationRequest;
+ }
+};
+
+void tst_QWebPage::acceptNavigationRequest()
+{
+ QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
+
+ NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
+ m_view->setPage(newPage);
+
+ m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
+ "<input type='text'><input type='submit'></form></body></html>"), QUrl());
+ QTRY_COMPARE(loadSpy.count(), 1);
+
+ m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
+
+ newPage->m_acceptNavigationRequest = true;
+ m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
+ QTRY_COMPARE(loadSpy.count(), 2);
+
+ QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
+
+ // Restore default page
+ m_view->setPage(0);
+}
+
+class JSTestPage : public QWebPage
+{
+Q_OBJECT
+public:
+ JSTestPage(QObject* parent = 0)
+ : QWebPage(parent) {}
+
+ virtual bool shouldInterruptJavaScript()
+ {
+ return true;
+ }
+public Q_SLOTS:
+ void requestPermission(QWebFrame* frame, QWebPage::Feature feature)
+ {
+ if (m_allowGeolocation)
+ setFeaturePermission(frame, feature, PermissionGrantedByUser);
+ else
+ setFeaturePermission(frame, feature, PermissionDeniedByUser);
+ }
+
+public:
+ void setGeolocationPermission(bool allow)
+ {
+ m_allowGeolocation = allow;
+ }
+
+private:
+ bool m_allowGeolocation;
+};
+
+// [Qt] tst_QWebPage::infiniteLoopJS() timeouts with DFG JIT
+// https://bugs.webkit.org/show_bug.cgi?id=79040
+/*
+void tst_QWebPage::infiniteLoopJS()
+{
+ JSTestPage* newPage = new JSTestPage(m_view);
+ m_view->setPage(newPage);
+ m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
+ m_view->page()->mainFrame()->evaluateJavaScript("var run = true; var a = 1; while (run) { a++; }");
+ delete newPage;
+}
+*/
+
+void tst_QWebPage::geolocationRequestJS()
+{
+ JSTestPage* newPage = new JSTestPage(m_view);
+
+ if (newPage->mainFrame()->evaluateJavaScript(QLatin1String("!navigator.geolocation")).toBool()) {
+ delete newPage;
+ W_QSKIP("Geolocation is not supported.", SkipSingle);
+ }
+
+ connect(newPage, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)),
+ newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
+
+ newPage->setGeolocationPermission(false);
+ m_view->setPage(newPage);
+ m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
+ m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)");
+ QTest::qWait(2000);
+ QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
+
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=102235", Continue);
+ QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
+
+ newPage->setGeolocationPermission(true);
+ m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);");
+ empty = m_view->page()->mainFrame()->evaluateJavaScript("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;
+}
+
+void tst_QWebPage::loadFinished()
+{
+ qRegisterMetaType<QWebFrame*>("QWebFrame*");
+ qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
+ QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
+ QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
+
+ m_view->page()->mainFrame()->load(QUrl("data:text/html,<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>"));
+ QTRY_COMPARE(spyLoadFinished.count(), 1);
+
+ QTRY_VERIFY(spyLoadStarted.count() > 1);
+ QTRY_VERIFY(spyLoadFinished.count() > 1);
+
+ spyLoadFinished.clear();
+
+ m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
+ "foo \"><frame src=\"data:text/html,bar\"></frameset>"));
+ QTRY_COMPARE(spyLoadFinished.count(), 1);
+ QCOMPARE(spyLoadFinished.count(), 1);
+}
+
+void tst_QWebPage::actionStates()
+{
+ QWebPage* page = m_view->page();
+
+ page->mainFrame()->load(QUrl("qrc:///resources/script.html"));
+
+ QAction* reloadAction = page->action(QWebPage::Reload);
+ QAction* stopAction = page->action(QWebPage::Stop);
+
+ QTRY_VERIFY(reloadAction->isEnabled());
+ QTRY_VERIFY(!stopAction->isEnabled());
+}
+
+class ConsolePage : public QWebPage
+{
+public:
+ ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
+
+ virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
+ {
+ messages.append(message);
+ lineNumbers.append(lineNumber);
+ sourceIDs.append(sourceID);
+ }
+
+ QStringList messages;
+ QList<int> lineNumbers;
+ QStringList sourceIDs;
+};
+
+void tst_QWebPage::consoleOutput()
+{
+ ConsolePage page;
+ page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
+ QCOMPARE(page.messages.count(), 1);
+ QCOMPARE(page.lineNumbers.at(0), 1);
+}
+
+class TestPage : public QWebPage {
+ Q_OBJECT
+public:
+ TestPage(QObject* parent = 0) : QWebPage(parent)
+ {
+ connect(this, SIGNAL(geometryChangeRequested(QRect)), this, SLOT(slotGeometryChangeRequested(QRect)));
+ }
+
+ struct Navigation {
+ QPointer<QWebFrame> frame;
+ QNetworkRequest request;
+ NavigationType type;
+ };
+
+ QList<Navigation> navigations;
+ QList<TestPage*> createdWindows;
+ QRect requestedGeometry;
+
+ virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
+ Navigation n;
+ n.frame = frame;
+ n.request = request;
+ n.type = type;
+ navigations.append(n);
+ return true;
+ }
+
+ virtual QWebPage* createWindow(WebWindowType) {
+ TestPage* page = new TestPage(this);
+ createdWindows.append(page);
+ return page;
+ }
+
+private Q_SLOTS:
+ void slotGeometryChangeRequested(const QRect& geom) {
+ requestedGeometry = geom;
+ }
+};
+
+void tst_QWebPage::popupFormSubmission()
+{
+ TestPage page;
+ page.settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
+ page.mainFrame()->setHtml("<form name=form1 method=get action='' target=myNewWin>"\
+ "<input type=hidden name=foo value='bar'>"\
+ "</form>");
+ page.mainFrame()->evaluateJavaScript("window.open('', 'myNewWin', 'width=500,height=300,toolbar=0')");
+ page.mainFrame()->evaluateJavaScript("document.form1.submit();");
+
+ QTest::qWait(500);
+ // The number of popup created should be one.
+ QVERIFY(page.createdWindows.size() == 1);
+
+ QString url = page.createdWindows.takeFirst()->mainFrame()->url().toString();
+ // Check if the form submission was OK.
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118597", Continue);
+ QVERIFY(url.contains("?foo=bar"));
+}
+
+void tst_QWebPage::acceptNavigationRequestWithNewWindow()
+{
+ TestPage* page = new TestPage(m_view);
+ page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
+ m_page = page;
+ m_view->setPage(m_page);
+
+ m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QFocusEvent fe(QEvent::FocusIn);
+ m_page->event(&fe);
+
+ QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
+
+ QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
+ m_page->event(&keyEnter);
+
+ QCOMPARE(page->navigations.count(), 2);
+
+ TestPage::Navigation n = page->navigations.at(1);
+ QVERIFY(n.frame.isNull());
+ QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
+ QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
+
+ QCOMPARE(page->createdWindows.count(), 1);
+}
+
+class TestNetworkManager : public QNetworkAccessManager
+{
+public:
+ TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
+
+ QList<QUrl> requestedUrls;
+ QList<QNetworkRequest> requests;
+
+protected:
+ virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
+ requests.append(request);
+ requestedUrls.append(request.url());
+ return QNetworkAccessManager::createRequest(op, request, outgoingData);
+ }
+};
+
+void tst_QWebPage::userStyleSheet()
+{
+ TestNetworkManager* networkManager = new TestNetworkManager(m_page);
+ m_page->setNetworkAccessManager(networkManager);
+
+ m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
+ + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
+ m_view->setHtml("<p>hello world</p>");
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(networkManager->requestedUrls.count() >= 1);
+ QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
+}
+
+void tst_QWebPage::userStyleSheetFromLocalFileUrl()
+{
+ TestNetworkManager* networkManager = new TestNetworkManager(m_page);
+ m_page->setNetworkAccessManager(networkManager);
+
+ QUrl styleSheetUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebpage/resources/user.css"));
+ m_page->settings()->setUserStyleSheetUrl(styleSheetUrl);
+ m_view->setHtml("<p>hello world</p>");
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(networkManager->requestedUrls.count() >= 1);
+ QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
+}
+
+void tst_QWebPage::userStyleSheetFromQrcUrl()
+{
+ TestNetworkManager* networkManager = new TestNetworkManager(m_page);
+ m_page->setNetworkAccessManager(networkManager);
+
+ m_page->settings()->setUserStyleSheetUrl(QUrl("qrc:///resources/user.css"));
+ m_view->setHtml("<p>hello world</p>");
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(networkManager->requestedUrls.count() >= 1);
+ QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
+}
+
+void tst_QWebPage::loadHtml5Video()
+{
+#if defined(WTF_USE_QT_MULTIMEDIA) && WTF_USE_QT_MULTIMEDIA
+ QByteArray url("http://does.not/exist?a=1%2Cb=2");
+ m_view->setHtml("<p><video id ='video' src='" + url + "' autoplay/></p>");
+ QTest::qWait(2000);
+ QUrl mUrl = DumpRenderTreeSupportQt::mediaContentUrlByElementId(m_page->mainFrame()->handle(), "video");
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=65452", Continue);
+ QCOMPARE(mUrl.toEncoded(), url);
+#else
+ W_QSKIP("This test requires Qt Multimedia", SkipAll);
+#endif
+}
+
+void tst_QWebPage::viewModes()
+{
+ m_view->setHtml("<body></body>");
+ m_page->setProperty("_q_viewMode", "minimized");
+
+ QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
+ QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
+
+ QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
+ QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
+
+ QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
+ QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
+}
+
+void tst_QWebPage::modified()
+{
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(!m_page->isModified());
+
+// m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
+ m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
+ m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
+
+ QVERIFY(m_page->isModified());
+
+ m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
+
+ QVERIFY(!m_page->isModified());
+
+ m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
+
+ QVERIFY(m_page->isModified());
+
+ QVERIFY(m_page->history()->canGoBack());
+ QVERIFY(!m_page->history()->canGoForward());
+ QCOMPARE(m_page->history()->count(), 2);
+ QVERIFY(m_page->history()->backItem().isValid());
+ QVERIFY(!m_page->history()->forwardItem().isValid());
+
+ m_page->history()->back();
+ QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(!m_page->history()->canGoBack());
+ QVERIFY(m_page->history()->canGoForward());
+
+ QVERIFY(!m_page->isModified());
+
+ QVERIFY(m_page->history()->currentItemIndex() == 0);
+
+ m_page->history()->setMaximumItemCount(3);
+ QVERIFY(m_page->history()->maximumItemCount() == 3);
+
+ QVariant variant("string test");
+ m_page->history()->currentItem().setUserData(variant);
+ QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
+
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
+ QVERIFY(m_page->history()->count() == 2);
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
+ QVERIFY(m_page->history()->count() == 2);
+ m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
+ QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
+}
+
+// https://bugs.webkit.org/show_bug.cgi?id=51331
+void tst_QWebPage::updatePositionDependentActionsCrash()
+{
+ QWebView view;
+ view.setHtml("<p>test");
+ QPoint pos(0, 0);
+ view.page()->updatePositionDependentActions(pos);
+ QMenu* contextMenu = 0;
+ foreach (QObject* child, view.children()) {
+ contextMenu = qobject_cast<QMenu*>(child);
+ if (contextMenu)
+ break;
+ }
+ QVERIFY(!contextMenu);
+}
+
+// https://bugs.webkit.org/show_bug.cgi?id=20357
+void tst_QWebPage::contextMenuCrash()
+{
+ QWebView view;
+ view.setHtml("<p>test");
+ QPoint pos(0, 0);
+ QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
+ view.page()->swallowContextMenuEvent(&event);
+ view.page()->updatePositionDependentActions(pos);
+ QMenu* contextMenu = 0;
+ foreach (QObject* child, view.children()) {
+ contextMenu = qobject_cast<QMenu*>(child);
+ if (contextMenu)
+ break;
+ }
+ QVERIFY(contextMenu);
+ delete contextMenu;
+}
+
+void tst_QWebPage::database()
+{
+ QString path = tmpDirPath();
+ m_page->settings()->setOfflineStoragePath(path);
+ QVERIFY(m_page->settings()->offlineStoragePath() == path);
+
+ QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
+ QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
+
+ m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
+ m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
+
+ QString dbFileName = path + "Databases.db";
+
+ if (QFile::exists(dbFileName))
+ QFile::remove(dbFileName);
+
+ qRegisterMetaType<QWebFrame*>("QWebFrame*");
+ QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
+ m_view->setHtml(QString("<html><head><script>var db; db=openDatabase('testdb', '1.0', 'test database API', 50000); </script></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
+ QTRY_COMPARE(spy.count(), 1);
+ m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
+ QTRY_COMPARE(spy.count(),1);
+
+ m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
+ m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
+
+ QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
+ QCOMPARE(s1.toString(), QString("This is a test for local storage"));
+
+ m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
+ m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
+ QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
+ QCOMPARE(s2.toString(), QString("This is a test for session storage"));
+
+ m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
+ m_page->mainFrame()->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });");
+ QTest::qWait(200);
+
+ // Remove all databases.
+ QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
+ QList<QWebDatabase> dbs = origin.databases();
+ for (int i = 0; i < dbs.count(); i++) {
+ QString fileName = dbs[i].fileName();
+ QVERIFY(QFile::exists(fileName));
+ QWebDatabase::removeDatabase(dbs[i]);
+ QVERIFY(!QFile::exists(fileName));
+ }
+ QVERIFY(!origin.databases().size());
+ // Remove removed test :-)
+ QWebDatabase::removeAllDatabases();
+ QVERIFY(!origin.databases().size());
+}
+
+class PluginPage : public QWebPage
+{
+public:
+ PluginPage(QObject *parent = 0)
+ : QWebPage(parent) {}
+
+ struct CallInfo
+ {
+ CallInfo(const QString &c, const QUrl &u,
+ const QStringList &pn, const QStringList &pv,
+ QObject *r)
+ : classid(c), url(u), paramNames(pn),
+ paramValues(pv), returnValue(r)
+ {}
+ QString classid;
+ QUrl url;
+ QStringList paramNames;
+ QStringList paramValues;
+ QObject *returnValue;
+ };
+
+ QList<CallInfo> calls;
+
+protected:
+ virtual QObject *createPlugin(const QString &classid, const QUrl &url,
+ const QStringList &paramNames,
+ const QStringList &paramValues)
+ {
+ QObject *result = 0;
+ if (classid == "pushbutton")
+ result = new QPushButton();
+#ifndef QT_NO_INPUTDIALOG
+ else if (classid == "lineedit")
+ result = new QLineEdit();
+#endif
+ else if (classid == "graphicswidget")
+ result = new QGraphicsWidget();
+ if (result)
+ result->setObjectName(classid);
+ calls.append(CallInfo(classid, url, paramNames, paramValues, result));
+ return result;
+ }
+};
+
+static void createPlugin(QWebView *view)
+{
+ QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
+
+ PluginPage* newPage = new PluginPage(view);
+ view->setPage(newPage);
+
+ // type has to be application/x-qt-plugin
+ view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(newPage->calls.count(), 0);
+
+ view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QCOMPARE(newPage->calls.count(), 1);
+ {
+ PluginPage::CallInfo ci = newPage->calls.takeFirst();
+ QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
+ QCOMPARE(ci.url, QUrl());
+ QCOMPARE(ci.paramNames.count(), 3);
+ QCOMPARE(ci.paramValues.count(), 3);
+ QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
+ QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
+ QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
+ QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
+ QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
+ QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
+ QVERIFY(ci.returnValue != 0);
+ QVERIFY(ci.returnValue->inherits("QPushButton"));
+ }
+ // test JS bindings
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
+ QString::fromLatin1("[object HTMLObjectElement]"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
+ QString::fromLatin1("[object HTMLObjectElement]"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
+ QString::fromLatin1("string"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
+ QString::fromLatin1("pushbutton"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
+ QString::fromLatin1("function"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
+ QString::fromLatin1("function clicked() {\n [native code]\n}"));
+
+ view->setHtml(QString("<html><body><table>"
+ "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
+ "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
+ "</table></body></html>"), QUrl("http://foo.bar.baz"));
+ QTRY_COMPARE(loadSpy.count(), 3);
+ QCOMPARE(newPage->calls.count(), 2);
+ {
+ PluginPage::CallInfo ci = newPage->calls.takeFirst();
+ QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
+ QCOMPARE(ci.url, QUrl());
+ QCOMPARE(ci.paramNames.count(), 3);
+ QCOMPARE(ci.paramValues.count(), 3);
+ QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
+ QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
+ QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
+ QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
+ QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
+ QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
+ QVERIFY(ci.returnValue != 0);
+ QVERIFY(ci.returnValue->inherits("QLineEdit"));
+ }
+ {
+ PluginPage::CallInfo ci = newPage->calls.takeFirst();
+ QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
+ QCOMPARE(ci.url, QUrl());
+ QCOMPARE(ci.paramNames.count(), 3);
+ QCOMPARE(ci.paramValues.count(), 3);
+ QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
+ QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
+ QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
+ QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
+ QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
+ QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
+ QVERIFY(ci.returnValue != 0);
+ QVERIFY(ci.returnValue->inherits("QPushButton"));
+ }
+}
+
+void tst_QWebPage::graphicsWidgetPlugin()
+{
+ m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+ QGraphicsWebView webView;
+
+ QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool)));
+
+ PluginPage* newPage = new PluginPage(&webView);
+ webView.setPage(newPage);
+
+ // type has to be application/x-qt-plugin
+ webView.setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(newPage->calls.count(), 0);
+
+ webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QCOMPARE(newPage->calls.count(), 1);
+ {
+ PluginPage::CallInfo ci = newPage->calls.takeFirst();
+ QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget"));
+ QCOMPARE(ci.url, QUrl());
+ QCOMPARE(ci.paramNames.count(), 3);
+ QCOMPARE(ci.paramValues.count(), 3);
+ QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
+ QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
+ QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
+ QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget"));
+ QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
+ QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget"));
+ QVERIFY(ci.returnValue);
+ QVERIFY(ci.returnValue->inherits("QGraphicsWidget"));
+ }
+ // test JS bindings
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(),
+ QString::fromLatin1("[object HTMLObjectElement]"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.toString()").toString(),
+ QString::fromLatin1("[object HTMLObjectElement]"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(),
+ QString::fromLatin1("string"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.objectName").toString(),
+ QString::fromLatin1("graphicswidget"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(),
+ QString::fromLatin1("function"));
+ QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(),
+ QString::fromLatin1("function geometryChanged() {\n [native code]\n}"));
+}
+
+void tst_QWebPage::createPluginWithPluginsEnabled()
+{
+ m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+ createPlugin(m_view);
+}
+
+void tst_QWebPage::createPluginWithPluginsDisabled()
+{
+ // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is
+ // false. The client decides whether a Qt plugin is enabled or not when
+ // it decides whether or not to instantiate it.
+ m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
+ createPlugin(m_view);
+}
+
+// Standard base class for template PluginTracerPage. In tests it is used as interface.
+class PluginCounterPage : public QWebPage {
+public:
+ int m_count;
+ QPointer<QObject> m_widget;
+ QObject* m_pluginParent;
+ PluginCounterPage(QObject* parent = 0)
+ : QWebPage(parent)
+ , m_count(0)
+ , m_pluginParent(0)
+ {
+ settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+ }
+ ~PluginCounterPage()
+ {
+ if (m_pluginParent)
+ m_pluginParent->deleteLater();
+ }
+};
+
+template<class T>
+class PluginTracerPage : public PluginCounterPage {
+public:
+ PluginTracerPage(QObject* parent = 0)
+ : PluginCounterPage(parent)
+ {
+ // this is a dummy parent object for the created plugin
+ m_pluginParent = new T;
+ }
+ virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
+ {
+ m_count++;
+ m_widget = new T;
+ // need a cast to the specific type, as QObject::setParent cannot be called,
+ // because it is not virtual. Instead it is necessary to call QWidget::setParent,
+ // which also takes a QWidget* instead of a QObject*. Therefore we need to
+ // upcast to T*, which is a QWidget.
+ static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
+ return m_widget.data();
+ }
+};
+
+class PluginFactory {
+public:
+ enum FactoredType {QWidgetType, QGraphicsWidgetType};
+ static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
+ {
+ PluginCounterPage* result = 0;
+ switch (type) {
+ case QWidgetType:
+ result = new PluginTracerPage<QWidget>(parent);
+ break;
+ case QGraphicsWidgetType:
+ result = new PluginTracerPage<QGraphicsWidget>(parent);
+ break;
+ default: {/*Oops*/};
+ }
+ return result;
+ }
+
+ static void prepareTestData()
+ {
+ QTest::addColumn<int>("type");
+ QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
+ QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
+ }
+};
+
+void tst_QWebPage::destroyPlugin_data()
+{
+ PluginFactory::prepareTestData();
+}
+
+void tst_QWebPage::destroyPlugin()
+{
+ QFETCH(int, type);
+ PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
+ m_view->setPage(page);
+
+ // we create the plugin, so the widget should be constructed
+ QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
+ m_view->setHtml(content);
+ QVERIFY(page->m_widget);
+ QCOMPARE(page->m_count, 1);
+
+ // navigate away, the plugin widget should be destructed
+ m_view->setHtml("<html><body>Hi</body></html>");
+ QTestEventLoop::instance().enterLoop(1);
+ QVERIFY(!page->m_widget);
+}
+
+void tst_QWebPage::createViewlessPlugin_data()
+{
+ PluginFactory::prepareTestData();
+}
+
+void tst_QWebPage::createViewlessPlugin()
+{
+ QFETCH(int, type);
+ PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
+ QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
+ page->mainFrame()->setHtml(content);
+ QCOMPARE(page->m_count, 1);
+ QVERIFY(page->m_widget);
+ QVERIFY(page->m_pluginParent);
+ QVERIFY(page->m_widget.data()->parent() == page->m_pluginParent);
+ delete page;
+
+}
+
+void tst_QWebPage::multiplePageGroupsAndLocalStorage()
+{
+ QDir dir(tmpDirPath());
+ dir.mkdir("path1");
+ dir.mkdir("path2");
+
+ QWebView view1;
+ QWebView view2;
+
+ view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
+ view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(tmpDirPath() + "/path1"));
+ DumpRenderTreeSupportQt::webPageSetGroupName(view1.page()->handle(), "group1");
+ view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
+ view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(tmpDirPath() + "/path2"));
+ DumpRenderTreeSupportQt::webPageSetGroupName(view2.page()->handle(), "group2");
+ QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()->handle()), QString("group1"));
+ QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()->handle()), QString("group2"));
+
+
+ view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
+ view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
+
+ view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
+ view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
+
+ view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
+ view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
+
+ QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
+ QCOMPARE(s1.toString(), QString("value1"));
+
+ QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
+ QCOMPARE(s2.toString(), QString("value2"));
+
+ QTest::qWait(1000);
+
+ QFile::remove(QDir::toNativeSeparators(tmpDirPath() + "/path1/http_www.myexample.com_0.localstorage"));
+ QFile::remove(QDir::toNativeSeparators(tmpDirPath() + "/path2/http_www.myexample.com_0.localstorage"));
+ dir.rmdir(QDir::toNativeSeparators("./path1"));
+ dir.rmdir(QDir::toNativeSeparators("./path2"));
+}
+
+class CursorTrackedPage : public QWebPage
+{
+public:
+
+ CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
+ setViewportSize(QSize(1024, 768)); // big space
+ }
+
+ QString selectedText() {
+ return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
+ }
+
+ int selectionStartOffset() {
+ return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
+ }
+
+ int selectionEndOffset() {
+ return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
+ }
+
+ // true if start offset == end offset, i.e. no selected text
+ int isSelectionCollapsed() {
+ return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
+ }
+};
+
+void tst_QWebPage::cursorMovements()
+{
+ 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->mainFrame()->setHtml(content);
+
+ // this will select the first paragraph
+ QString script = "var range = document.createRange(); " \
+ "var node = document.getElementById(\"one\"); " \
+ "range.selectNode(node); " \
+ "getSelection().addRange(range);";
+ page->mainFrame()->evaluateJavaScript(script);
+ QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
+
+ QRegExp regExp(" style=\".*\"");
+ regExp.setMinimal(true);
+ QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>"));
+
+ // these actions must exist
+ QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
+ QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
+ QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
+ QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
+ QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
+ QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
+ QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
+ QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
+ QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
+ QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
+ QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
+ QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
+
+ // right now they are disabled because contentEditable is false
+ QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
+
+ // make it editable before navigating the cursor
+ page->setContentEditable(true);
+
+ // here the actions are enabled after contentEditable is true
+ QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
+
+ // cursor will be before the word "jump"
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // cursor will be between 'j' and 'u' in the word "jump"
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 1);
+
+ // cursor will be between 'u' and 'm' in the word "jump"
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 2);
+
+ // cursor will be after the word "jump"
+ page->triggerAction(QWebPage::MoveToNextWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 5);
+
+ // cursor will be after the word "lazy"
+ page->triggerAction(QWebPage::MoveToNextWord);
+ page->triggerAction(QWebPage::MoveToNextWord);
+ page->triggerAction(QWebPage::MoveToNextWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 19);
+
+ // cursor will be between 'z' and 'y' in "lazy"
+ page->triggerAction(QWebPage::MoveToPreviousChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 18);
+
+ // cursor will be between 'a' and 'z' in "lazy"
+ page->triggerAction(QWebPage::MoveToPreviousChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 17);
+
+ // cursor will be before the word "lazy"
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 15);
+
+ // cursor will be before the word "quick"
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 4);
+
+ // cursor will be between 'p' and 's' in the word "jumps"
+ page->triggerAction(QWebPage::MoveToNextWord);
+ page->triggerAction(QWebPage::MoveToNextWord);
+ page->triggerAction(QWebPage::MoveToNextWord);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 4);
+
+ // cursor will be before the word "jumps"
+ page->triggerAction(QWebPage::MoveToStartOfLine);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // cursor will be after the word "dog"
+ page->triggerAction(QWebPage::MoveToEndOfLine);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 23);
+
+ // cursor will be between 'w' and 'n' in "brown"
+ page->triggerAction(QWebPage::MoveToStartOfLine);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 14);
+
+ // cursor will be after the word "fox"
+ page->triggerAction(QWebPage::MoveToEndOfLine);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 19);
+
+ // cursor will be before the word "The"
+ page->triggerAction(QWebPage::MoveToStartOfDocument);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // cursor will be after the word "you!"
+ page->triggerAction(QWebPage::MoveToEndOfDocument);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 12);
+
+ // cursor will be before the word "be"
+ page->triggerAction(QWebPage::MoveToStartOfBlock);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // cursor will be after the word "you!"
+ page->triggerAction(QWebPage::MoveToEndOfBlock);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 12);
+
+ // try to move before the document start
+ page->triggerAction(QWebPage::MoveToStartOfDocument);
+ page->triggerAction(QWebPage::MoveToPreviousChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+ page->triggerAction(QWebPage::MoveToStartOfDocument);
+ page->triggerAction(QWebPage::MoveToPreviousWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // try to move past the document end
+ page->triggerAction(QWebPage::MoveToEndOfDocument);
+ page->triggerAction(QWebPage::MoveToNextChar);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 12);
+ page->triggerAction(QWebPage::MoveToEndOfDocument);
+ page->triggerAction(QWebPage::MoveToNextWord);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 12);
+
+ delete page;
+}
+
+void tst_QWebPage::textSelection()
+{
+ 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->mainFrame()->setHtml(content);
+
+ // these actions must exist
+ QVERIFY(page->action(QWebPage::SelectAll) != 0);
+ QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
+ QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
+ QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
+ QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
+ QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
+ QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
+ QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
+ QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
+ QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
+ QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
+ QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
+ QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
+
+ // right now they are disabled because contentEditable is false and
+ // there isn't an existing selection to modify
+ QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
+
+ // ..but SelectAll is awalys enabled
+ QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
+
+ // Verify hasSelection returns false since there is no selection yet...
+ QCOMPARE(page->hasSelection(), false);
+
+ // this will select the first paragraph
+ QString selectScript = "var range = document.createRange(); " \
+ "var node = document.getElementById(\"one\"); " \
+ "range.selectNode(node); " \
+ "getSelection().addRange(range);";
+ page->mainFrame()->evaluateJavaScript(selectScript);
+ QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
+ QRegExp regExp(" style=\".*\"");
+ regExp.setMinimal(true);
+ QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>"));
+
+ // Make sure hasSelection returns true, since there is selected text now...
+ QCOMPARE(page->hasSelection(), true);
+
+ // here the actions are enabled after a selection has been created
+ QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
+
+ // make it editable before navigating the cursor
+ page->setContentEditable(true);
+
+ // cursor will be before the word "The", this makes sure there is a charet
+ page->triggerAction(QWebPage::MoveToStartOfDocument);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // here the actions are enabled after contentEditable is true
+ QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
+
+ delete page;
+}
+
+void tst_QWebPage::textEditing()
+{
+ 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->mainFrame()->setHtml(content);
+
+ // these actions must exist
+ QVERIFY(page->action(QWebPage::Cut) != 0);
+ QVERIFY(page->action(QWebPage::Copy) != 0);
+ QVERIFY(page->action(QWebPage::Paste) != 0);
+ QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
+ QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
+ QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
+ QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
+ QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
+ QVERIFY(page->action(QWebPage::ToggleBold) != 0);
+ QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
+ QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
+ QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
+ QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
+ QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
+ QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
+ QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
+ QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
+ QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
+ QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
+ QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
+ QVERIFY(page->action(QWebPage::Indent) != 0);
+ QVERIFY(page->action(QWebPage::Outdent) != 0);
+ QVERIFY(page->action(QWebPage::AlignCenter) != 0);
+ QVERIFY(page->action(QWebPage::AlignJustified) != 0);
+ QVERIFY(page->action(QWebPage::AlignLeft) != 0);
+ QVERIFY(page->action(QWebPage::AlignRight) != 0);
+
+ // right now they are disabled because contentEditable is false
+ QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
+
+ // Select everything
+ page->triggerAction(QWebPage::SelectAll);
+
+ // make sure it is enabled since there is a selection
+ QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
+
+ // make it editable before navigating the cursor
+ page->setContentEditable(true);
+
+ // clear the selection
+ page->triggerAction(QWebPage::MoveToStartOfDocument);
+ QVERIFY(page->isSelectionCollapsed());
+ QCOMPARE(page->selectionStartOffset(), 0);
+
+ // make sure it is disabled since there isn't a selection
+ QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
+
+ // here the actions are enabled after contentEditable is true
+ QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
+
+ // make sure these are disabled since there isn't a selection
+ QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
+ QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
+
+ // make sure everything is selected
+ page->triggerAction(QWebPage::SelectAll);
+
+ // this is only true if there is an editable selection
+ QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
+ QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
+
+ delete page;
+}
+
+void tst_QWebPage::requestCache()
+{
+ TestPage page;
+ QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+
+ page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_COMPARE(page.navigations.count(), 1);
+
+ page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QTRY_COMPARE(page.navigations.count(), 2);
+
+ page.triggerAction(QWebPage::Stop);
+ QVERIFY(page.history()->canGoBack());
+ page.triggerAction(QWebPage::Back);
+
+ QTRY_COMPARE(loadSpy.count(), 3);
+ QTRY_COMPARE(page.navigations.count(), 3);
+ QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
+ (int)QNetworkRequest::PreferNetwork);
+ QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
+ (int)QNetworkRequest::PreferNetwork);
+ QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
+ (int)QNetworkRequest::PreferCache);
+}
+
+void tst_QWebPage::loadCachedPage()
+{
+ TestPage page;
+ QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+ page.settings()->setMaximumPagesInCache(3);
+
+ page.mainFrame()->load(QUrl("data:text/html,This is first page"));
+
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_COMPARE(page.navigations.count(), 1);
+
+ QUrl firstPageUrl = page.mainFrame()->url();
+ page.mainFrame()->load(QUrl("data:text/html,This is second page"));
+
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QTRY_COMPARE(page.navigations.count(), 2);
+
+ page.triggerAction(QWebPage::Stop);
+ QVERIFY(page.history()->canGoBack());
+
+ QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
+ QVERIFY(urlSpy.isValid());
+
+ page.triggerAction(QWebPage::Back);
+ ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
+ QCOMPARE(urlSpy.size(), 1);
+
+ QList<QVariant> arguments1 = urlSpy.takeFirst();
+ QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl);
+
+}
+void tst_QWebPage::backActionUpdate()
+{
+ QWebView view;
+ QWebPage *page = view.page();
+ QAction *action = page->action(QWebPage::Back);
+ QVERIFY(!action->isEnabled());
+ QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
+ QUrl url = QUrl("qrc:///resources/framedindex.html");
+ page->mainFrame()->load(url);
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QVERIFY(!action->isEnabled());
+ QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
+ QTRY_COMPARE(loadSpy.count(), 2);
+
+ QVERIFY(action->isEnabled());
+}
+
+void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
+{
+ if (!webFrame)
+ return;
+
+ framePosition += QPoint(webFrame->pos());
+ QList<QWebFrame*> 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()));
+ }
+}
+
+void tst_QWebPage::frameAt()
+{
+ QWebView webView;
+ QWebPage* webPage = webView.page();
+ QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
+ QUrl url = QUrl("qrc:///resources/iframe.html");
+ webPage->mainFrame()->load(url);
+ QTRY_COMPARE(loadSpy.count(), 1);
+ frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
+}
+
+void tst_QWebPage::inputMethods_data()
+{
+ QTest::addColumn<QString>("viewType");
+ QTest::newRow("QWebView") << "QWebView";
+ QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
+}
+
+static Qt::InputMethodHints inputMethodHints(QObject* object)
+{
+ if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
+ return o->inputMethodHints();
+ if (QWidget* w = qobject_cast<QWidget*>(object))
+ return w->inputMethodHints();
+ return Qt::InputMethodHints();
+}
+
+static bool inputMethodEnabled(QObject* object)
+{
+ if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
+ return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
+ if (QWidget* w = qobject_cast<QWidget*>(object))
+ return w->testAttribute(Qt::WA_InputMethodEnabled);
+ return false;
+}
+
+static void clickOnPage(QWebPage* page, const QPoint& position)
+{
+ QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ page->event(&evpres);
+ QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ page->event(&evrel);
+}
+
+void tst_QWebPage::inputMethods()
+{
+ QFETCH(QString, viewType);
+ QWebPage* page = new QWebPage;
+ QObject* view = 0;
+ QObject* container = 0;
+ if (viewType == "QWebView") {
+ QWebView* wv = new QWebView;
+ wv->setPage(page);
+ view = wv;
+ container = view;
+ } else if (viewType == "QGraphicsWebView") {
+ QGraphicsWebView* wv = new QGraphicsWebView;
+ wv->setPage(page);
+ view = wv;
+
+ QGraphicsView* gv = new QGraphicsView;
+ QGraphicsScene* scene = new QGraphicsScene(gv);
+ gv->setScene(scene);
+ scene->addItem(wv);
+ wv->setGeometry(QRect(0, 0, 500, 500));
+
+ container = gv;
+ } else
+ QVERIFY2(false, "Unknown view type");
+
+ page->settings()->setFontFamily(QWebSettings::SerifFont, page->settings()->fontFamily(QWebSettings::FixedFont));
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
+ "<input type='password'/>" \
+ "</body></html>");
+ page->mainFrame()->setFocus();
+
+ TestInputContext testContext;
+
+ QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
+ QPoint textInputCenter = inputs.at(0).geometry().center();
+
+ clickOnPage(page, textInputCenter);
+
+ // This part of the test checks if the SIP (Software Input Panel) is triggered,
+ // which normally happens on mobile platforms, when a user input form receives
+ // a mouse click.
+ int inputPanel = 0;
+ if (viewType == "QWebView") {
+ if (QWebView* wv = qobject_cast<QWebView*>(view))
+ inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
+ } else if (viewType == "QGraphicsWebView") {
+ if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view))
+ inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
+ }
+
+ // For non-mobile platforms RequestSoftwareInputPanel event is not called
+ // because there is no SIP (Software Input Panel) triggered. In the case of a
+ // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked
+ // and the RequestSoftwareInputPanel event is called. For these two situations
+ // this part of the test can verified as the checks below.
+ if (inputPanel)
+ QVERIFY(testContext.isInputPanelVisible());
+ else
+ QVERIFY(!testContext.isInputPanelVisible());
+ testContext.hideInputPanel();
+
+ clickOnPage(page, textInputCenter);
+ QVERIFY(testContext.isInputPanelVisible());
+
+ //ImMicroFocus
+ QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
+ QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
+
+ // We assigned the serif font famility to be the same as the fixef font family.
+ // Then test ImFont on a serif styled element, we should get our fixef font family.
+ variant = page->inputMethodQuery(Qt::ImFont);
+ QFont font = variant.value<QFont>();
+ QCOMPARE(page->settings()->fontFamily(QWebSettings::FixedFont), font.family());
+
+ QList<QInputMethodEvent::Attribute> inputAttributes;
+
+ //Insert text.
+ {
+ QInputMethodEvent eventText("QtWebKit", inputAttributes);
+ QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
+ page->event(&eventText);
+ QCOMPARE(signalSpy.count(), 0);
+ }
+
+ {
+ QInputMethodEvent eventText("", inputAttributes);
+ eventText.setCommitString(QString("QtWebKit"), 0, 0);
+ page->event(&eventText);
+ }
+
+ //ImMaximumTextLength
+ variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
+ QCOMPARE(20, variant.toInt());
+
+ //Set selection
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
+ QInputMethodEvent eventSelection("",inputAttributes);
+ page->event(&eventSelection);
+
+ //ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ int anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 3);
+
+ //ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ int cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 5);
+
+ //ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ QString selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString("eb"));
+
+ //Set selection with negative length
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
+ QInputMethodEvent eventSelection3("",inputAttributes);
+ page->event(&eventSelection3);
+
+ //ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 1);
+
+ //ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 6);
+
+ //ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString("tWebK"));
+
+ //ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ QString value = variant.value<QString>();
+ QCOMPARE(value, QString("QtWebKit"));
+
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ // Clear the selection, so the next test does not clear any contents.
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("composition", attributes);
+ page->event(&event);
+ }
+
+ // A ongoing composition should not change the surrounding text before it is committed.
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ value = variant.value<QString>();
+ QCOMPARE(value, QString("QtWebKit"));
+
+ // Cancel current composition first
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
+ QInputMethodEvent eventSelection4("", inputAttributes);
+ page->event(&eventSelection4);
+
+ // START - Tests for Selection when the Editor is NOT in Composition mode
+
+ // LEFT to RIGHT selection
+ // Deselect the selection by sending MouseButtonPress events
+ // This moves the current cursor to the end of the text
+ clickOnPage(page, textInputCenter);
+
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QString(), attributes);
+ event.setCommitString("XXX", 0, 0);
+ page->event(&event);
+ event.setCommitString(QString(), -2, 2); // Erase two characters.
+ page->event(&event);
+ event.setCommitString(QString(), -1, 1); // Erase one character.
+ page->event(&event);
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ value = variant.value<QString>();
+ QCOMPARE(value, QString("QtWebKit"));
+ }
+
+ //Move to the start of the line
+ page->triggerAction(QWebPage::MoveToStartOfLine);
+
+ QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
+ QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
+
+ //Move 2 characters RIGHT
+ for (int j = 0; j < 2; ++j) {
+ page->event(&keyRightEventPress);
+ page->event(&keyRightEventRelease);
+ }
+
+ //Select to the end of the line
+ page->triggerAction(QWebPage::SelectEndOfLine);
+
+ //ImAnchorPosition QtWebKit
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 2);
+
+ //ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 8);
+
+ //ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString("WebKit"));
+
+ //RIGHT to LEFT selection
+ //Deselect the selection (this moves the current cursor to the end of the text)
+ clickOnPage(page, textInputCenter);
+
+ //ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 8);
+
+ //ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 8);
+
+ //ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
+ QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
+
+ //Move 2 characters LEFT
+ for (int i = 0; i < 2; ++i) {
+ page->event(&keyLeftEventPress);
+ page->event(&keyLeftEventRelease);
+ }
+
+ //Select to the start of the line
+ page->triggerAction(QWebPage::SelectStartOfLine);
+
+ //ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 6);
+
+ //ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 0);
+
+ //ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString("QtWebK"));
+
+ //END - Tests for Selection when the Editor is not in Composition mode
+
+ //ImhHiddenText
+ QPoint passwordInputCenter = inputs.at(1).geometry().center();
+ clickOnPage(page, passwordInputCenter);
+
+ QVERIFY(inputMethodEnabled(view));
+ QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
+
+ clickOnPage(page, textInputCenter);
+ QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
+
+ page->mainFrame()->setHtml("<html><body><p>nothing to input here");
+ testContext.hideInputPanel();
+
+ QWebElement para = page->mainFrame()->findFirstElement("p");
+ clickOnPage(page, para.geometry().center());
+
+ QVERIFY(!testContext.isInputPanelVisible());
+
+ //START - Test for sending empty QInputMethodEvent
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input3' value='QtWebKit2'/>" \
+ "</body></html>");
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
+
+ //Send empty QInputMethodEvent
+ QInputMethodEvent emptyEvent;
+ page->event(&emptyEvent);
+
+ QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString();
+ QCOMPARE(inputValue, QString("QtWebKit2"));
+ //END - Test for sending empty QInputMethodEvent
+
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \
+ "</body></html>");
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
+
+ // Clear the selection, also cancel the ongoing composition if there is one.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ QString surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 0);
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 0);
+
+ // 1. Insert a character to the beginning of the line.
+ // Send temporary text, which makes the editor has composition 'm'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("m", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 0);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 0);
+
+ // Send temporary text, which makes the editor has composition 'n'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("n", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 0);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 0);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("o");
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 1);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 1);
+
+ // 2. insert a character to the middle of the line.
+ // Send temporary text, which makes the editor has composition 'd'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("d", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 1);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 1);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("e");
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 2);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 2);
+
+ // 3. Insert a character to the end of the line.
+ page->triggerAction(QWebPage::MoveToEndOfLine);
+
+ // Send temporary text, which makes the editor has composition 't'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("t", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 22);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 22);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("t");
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 23);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 23);
+
+ // 4. Replace the selection.
+ page->triggerAction(QWebPage::SelectPreviousWord);
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString("inputMethodt"));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 11);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 23);
+
+ // Send temporary text, which makes the editor has composition 'w'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("w", attributes);
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit "));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 11);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 11);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("2");
+ page->event(&event);
+ }
+
+ // ImCurrentSelection
+ variant = page->inputMethodQuery(Qt::ImCurrentSelection);
+ selectionValue = variant.value<QString>();
+ QCOMPARE(selectionValue, QString(""));
+
+ // ImSurroundingText
+ variant = page->inputMethodQuery(Qt::ImSurroundingText);
+ surroundingValue = variant.value<QString>();
+ QCOMPARE(surroundingValue, QString("oeQtWebKit 2"));
+
+ // ImCursorPosition
+ variant = page->inputMethodQuery(Qt::ImCursorPosition);
+ cursorPosition = variant.toInt();
+ QCOMPARE(cursorPosition, 12);
+
+ // ImAnchorPosition
+ variant = page->inputMethodQuery(Qt::ImAnchorPosition);
+ anchorPosition = variant.toInt();
+ QCOMPARE(anchorPosition, 12);
+
+ // Check sending RequestSoftwareInputPanel event
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input5' value='QtWebKit inputMethod'/>" \
+ "<div id='btnDiv' onclick='i=document.getElementById(&quot;input5&quot;); i.focus();'>abc</div>"\
+ "</body></html>");
+ QWebElement inputElement = page->mainFrame()->findFirstElement("div");
+ clickOnPage(page, inputElement.geometry().center());
+
+ QVERIFY(!testContext.isInputPanelVisible());
+
+ // START - Newline test for textarea
+ qApp->processEvents();
+ page->mainFrame()->setHtml("<html><body>" \
+ "<textarea rows='5' cols='1' id='input5' value=''/>" \
+ "</body></html>");
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.focus(); inputEle.select();");
+
+ // Enter Key without key text
+ QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
+ page->event(&keyEnter);
+ QList<QInputMethodEvent::Attribute> attribs;
+
+ QInputMethodEvent eventText(QString(), attribs);
+ eventText.setCommitString("\n");
+ page->event(&eventText);
+
+ QInputMethodEvent eventText2(QString(), attribs);
+ eventText2.setCommitString("third line");
+ page->event(&eventText2);
+ qApp->processEvents();
+
+ QString inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString("\n\nthird line"));
+
+ // Enter Key with key text '\r'
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString(""));
+
+ QKeyEvent keyEnterWithCarriageReturn(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\r");
+ page->event(&keyEnterWithCarriageReturn);
+ page->event(&eventText);
+ page->event(&eventText2);
+ qApp->processEvents();
+
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString("\n\nthird line"));
+
+ // Enter Key with key text '\n'
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString(""));
+
+ QKeyEvent keyEnterWithLineFeed(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\n");
+ page->event(&keyEnterWithLineFeed);
+ page->event(&eventText);
+ page->event(&eventText2);
+ qApp->processEvents();
+
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString("\n\nthird line"));
+
+ // Enter Key with key text "\n\r"
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString(""));
+
+ QKeyEvent keyEnterWithLFCR(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\n\r");
+ page->event(&keyEnterWithLFCR);
+ page->event(&eventText);
+ page->event(&eventText2);
+ qApp->processEvents();
+
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString("\n\nthird line"));
+
+ // Return Key without key text
+ page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString(""));
+
+ QKeyEvent keyReturn(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
+ page->event(&keyReturn);
+ page->event(&eventText);
+ page->event(&eventText2);
+ qApp->processEvents();
+
+ inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString();
+ QCOMPARE(inputValue2, QString("\n\nthird line"));
+
+ // END - Newline test for textarea
+
+ delete container;
+}
+
+void tst_QWebPage::inputMethodsTextFormat_data()
+{
+ QTest::addColumn<QString>("string");
+ QTest::addColumn<int>("start");
+ QTest::addColumn<int>("length");
+
+ QTest::newRow("") << QString("") << 0 << 0;
+ QTest::newRow("Q") << QString("Q") << 0 << 1;
+ QTest::newRow("Qt") << QString("Qt") << 0 << 1;
+ QTest::newRow("Qt") << QString("Qt") << 0 << 2;
+ QTest::newRow("Qt") << QString("Qt") << 1 << 1;
+ QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
+ QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
+ QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
+ QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
+ QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
+ QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
+ QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
+ QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
+}
+
+
+void tst_QWebPage::inputMethodsTextFormat()
+{
+ QWebPage* page = new QWebPage;
+ QWebView* view = new QWebView;
+ view->setPage(page);
+ page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
+ page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()");
+ page->mainFrame()->setFocus();
+ view->show();
+
+ QFETCH(QString, string);
+ QFETCH(int, start);
+ QFETCH(int, length);
+
+ QList<QInputMethodEvent::Attribute> attrs;
+ QTextCharFormat format;
+ format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ format.setUnderlineColor(Qt::red);
+ attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
+ QInputMethodEvent im(string, attrs);
+ page->event(&im);
+
+ QTest::qWait(1000);
+
+ delete view;
+}
+
+void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
+{
+ QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
+
+ PluginPage* newPage = new PluginPage(m_view);
+ m_view->setPage(newPage);
+
+ m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+
+ m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
+ QTRY_COMPARE(loadSpy.count(), 1);
+
+ newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
+
+ newPage->mainFrame()->evaluateJavaScript("testme('foo')");
+
+ DumpRenderTreeSupportQt::garbageCollectorCollect();
+
+ // don't crash!
+ newPage->mainFrame()->evaluateJavaScript("testme('bar')");
+}
+
+void tst_QWebPage::localURLSchemes()
+{
+ int i = QWebSecurityOrigin::localSchemes().size();
+
+ QWebSecurityOrigin::removeLocalScheme("file");
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
+ QWebSecurityOrigin::addLocalScheme("file");
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
+
+ QWebSecurityOrigin::removeLocalScheme("qrc");
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
+ QWebSecurityOrigin::addLocalScheme("qrc");
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
+
+ QString myscheme = "myscheme";
+ QWebSecurityOrigin::addLocalScheme(myscheme);
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
+ QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
+ QWebSecurityOrigin::removeLocalScheme(myscheme);
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
+ QWebSecurityOrigin::removeLocalScheme(myscheme);
+ QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
+}
+
+static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
+{
+ webPage.settings()->setAttribute(settingAttribute, settingValue);
+ return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
+}
+
+void tst_QWebPage::testOptionalJSObjects()
+{
+ // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
+ // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
+ // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
+ // a feature for one instance will not turn it on for another.
+
+ QWebPage webPage1;
+ QWebPage webPage2;
+
+ webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/"));
+ webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/"));
+
+ QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
+ QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
+ QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true);
+ QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
+ QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
+ QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
+
+ QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
+ QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true);
+ QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
+ QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
+}
+
+static inline bool checkLocalStorageVisibility(QWebPage& webPage, bool localStorageEnabled)
+{
+ webPage.settings()->setAttribute(QWebSettings::LocalStorageEnabled, localStorageEnabled);
+ return webPage.mainFrame()->evaluateJavaScript(QString("(window.localStorage != undefined)")).toBool();
+}
+
+void tst_QWebPage::testLocalStorageVisibility()
+{
+ // Local storage's visibility depends on its security origin, which depends on base url.
+ // Initially, it will test it with base urls that get a globally unique origin, which may not
+ // be able to use local storage even if the feature is enabled. Then later the same test is
+ // done but with urls that would get a valid origin, so local storage could be used.
+ // Before every test case it checks if local storage is not already visible.
+
+ QWebPage webPage;
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("invalid"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("://misparsed.com"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("about:blank"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("data:text/html,test"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("https://www.example.com"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("ftp://files.example.com"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
+
+ webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///path/to/index.html"));
+
+ QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
+ QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
+}
+
+void tst_QWebPage::testEnablePersistentStorage()
+{
+ QWebPage webPage;
+
+ // By default all persistent options should be disabled
+ QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
+ QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
+ QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
+ QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
+
+ QWebSettings::enablePersistentStorage();
+
+
+ QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
+ QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
+ QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
+
+ QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
+ QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
+ QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
+}
+
+void tst_QWebPage::defaultTextEncoding()
+{
+ QWebFrame* mainFrame = m_page->mainFrame();
+
+ QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
+ QVERIFY(!defaultCharset.isEmpty());
+ QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
+
+ m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
+ QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
+ QCOMPARE(charset, QString("utf-8"));
+ QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
+
+ m_page->settings()->setDefaultTextEncoding(QString());
+ charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
+ QVERIFY(!charset.isEmpty());
+ QCOMPARE(charset, defaultCharset);
+
+ QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
+ charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
+ QCOMPARE(charset, QString("utf-8"));
+ QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
+}
+
+class ErrorPage : public QWebPage
+{
+public:
+
+ ErrorPage(QWidget* parent = 0): QWebPage(parent)
+ {
+ }
+
+ virtual bool supportsExtension(Extension extension) const
+ {
+ return extension == ErrorPageExtension;
+ }
+
+ virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
+ {
+ ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
+
+ errorPage->contentType = "text/html";
+ errorPage->content = "error";
+ return true;
+ }
+};
+
+void tst_QWebPage::errorPageExtension()
+{
+ ErrorPage page;
+ m_view->setPage(&page);
+
+ QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
+
+ m_view->setUrl(QUrl("data:text/html,foo"));
+ QTRY_COMPARE(spyLoadFinished.count(), 1);
+
+ page.mainFrame()->setUrl(QUrl("http://non.existent/url"));
+ QTRY_COMPARE(spyLoadFinished.count(), 2);
+ QCOMPARE(page.mainFrame()->toPlainText(), QString("error"));
+ QCOMPARE(page.history()->count(), 2);
+ QCOMPARE(page.history()->currentItem().url(), QUrl("http://non.existent/url"));
+ QCOMPARE(page.history()->canGoBack(), true);
+ QCOMPARE(page.history()->canGoForward(), false);
+
+ page.triggerAction(QWebPage::Back);
+ QTRY_COMPARE(page.history()->canGoBack(), false);
+ QTRY_COMPARE(page.history()->canGoForward(), true);
+
+ page.triggerAction(QWebPage::Forward);
+ QTRY_COMPARE(page.history()->canGoBack(), true);
+ QTRY_COMPARE(page.history()->canGoForward(), false);
+
+ page.triggerAction(QWebPage::Back);
+ QTRY_COMPARE(page.history()->canGoBack(), false);
+ QTRY_COMPARE(page.history()->canGoForward(), true);
+ QTRY_COMPARE(page.history()->currentItem().url(), QUrl("data:text/html,foo"));
+
+ m_view->setPage(0);
+}
+
+void tst_QWebPage::errorPageExtensionInIFrames()
+{
+ ErrorPage page;
+ m_view->setPage(&page);
+
+ m_view->page()->mainFrame()->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);
+}
+
+void tst_QWebPage::errorPageExtensionInFrameset()
+{
+ 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);
+}
+
+void tst_QWebPage::errorPageExtensionLoadFinished()
+{
+ ErrorPage page;
+ m_view->setPage(&page);
+
+ QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
+ QSignalSpy spyFrameLoadFinished(m_view->page()->mainFrame(), SIGNAL(loadFinished(bool)));
+
+ m_view->setUrl(QUrl("data:text/html,foo"));
+ QTRY_COMPARE(spyLoadFinished.count(), 1);
+ QTRY_COMPARE(spyFrameLoadFinished.count(), 1);
+
+ const bool loadSucceded = spyLoadFinished.at(0).at(0).toBool();
+ QVERIFY(loadSucceded);
+ const bool frameLoadSucceded = spyFrameLoadFinished.at(0).at(0).toBool();
+ QVERIFY(frameLoadSucceded);
+
+ m_view->page()->mainFrame()->setUrl(QUrl("http://non.existent/url"));
+ QTRY_COMPARE(spyLoadFinished.count(), 2);
+ QTRY_COMPARE(spyFrameLoadFinished.count(), 2);
+
+ const bool nonExistantLoadSucceded = spyLoadFinished.at(1).at(0).toBool();
+ QVERIFY(nonExistantLoadSucceded);
+ const bool nonExistantFrameLoadSucceded = spyFrameLoadFinished.at(1).at(0).toBool();
+ QVERIFY(nonExistantFrameLoadSucceded);
+
+ m_view->setPage(0);
+}
+
+class FriendlyWebPage : public QWebPage
+{
+public:
+ friend class tst_QWebPage;
+};
+
+void tst_QWebPage::userAgentApplicationName()
+{
+ const QString oldApplicationName = QCoreApplication::applicationName();
+ FriendlyWebPage page;
+
+ const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
+ QCoreApplication::setApplicationName(applicationNameMarker);
+ QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
+
+ QCoreApplication::setApplicationName(oldApplicationName);
+}
+
+class CustomUserAgentWebPage : public QWebPage
+{
+public:
+ static const QLatin1String filteredUserAgent;
+protected:
+ virtual QString userAgentForUrl(const QUrl& url) const
+ {
+ 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_QWebPage::userAgentNewlineStripping()
+{
+ CustomUserAgentWebPage page;
+ QWebFrame* mainFrame = page.mainFrame();
+ mainFrame->setHtml("<html><body></body></html>");
+ QCOMPARE(mainFrame->evaluateJavaScript("navigator.userAgent").toString(), CustomUserAgentWebPage::filteredUserAgent);
+}
+
+void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
+{
+ {
+ QWebPage webPage;
+ }
+ {
+ QWebPage webPage;
+ webPage.selectedText();
+ }
+ {
+ QWebPage webPage;
+ webPage.selectedHtml();
+ }
+ {
+ QWebPage webPage;
+ webPage.triggerAction(QWebPage::Back, true);
+ }
+ {
+ QWebPage webPage;
+ QPoint pos(10,10);
+ webPage.updatePositionDependentActions(pos);
+ }
+}
+
+static void takeScreenshot(QWebPage* page)
+{
+ QWebFrame* mainFrame = page->mainFrame();
+ page->setViewportSize(mainFrame->contentsSize());
+ QImage image(page->viewportSize(), QImage::Format_ARGB32);
+ QPainter painter(&image);
+ mainFrame->render(&painter);
+ painter.end();
+}
+
+void tst_QWebPage::screenshot_data()
+{
+ QTest::addColumn<QString>("html");
+ QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
+ QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
+ QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
+}
+
+void tst_QWebPage::screenshot()
+{
+ 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);
+
+ QFETCH(QString, html);
+ QWebPage* page = new QWebPage;
+ page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+ QWebFrame* mainFrame = page->mainFrame();
+ mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
+ ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
+
+ // take screenshot without a view
+ takeScreenshot(page);
+
+ QWebView* view = new QWebView;
+ view->setPage(page);
+
+ // take screenshot when attached to a view
+ takeScreenshot(page);
+
+ delete page;
+ delete view;
+
+ QDir::setCurrent(QApplication::applicationDirPath());
+}
+
+#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
+// https://bugs.webkit.org/show_bug.cgi?id=54138
+static void webGLScreenshotWithoutView(bool accelerated)
+{
+ QWebPage page;
+ page.settings()->setAttribute(QWebSettings::WebGLEnabled, true);
+ page.settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, accelerated);
+ QWebFrame* mainFrame = page.mainFrame();
+ mainFrame->setHtml("<html><body>"
+ "<canvas id='webgl' width='300' height='300'></canvas>"
+ "<script>document.getElementById('webgl').getContext('experimental-webgl')</script>"
+ "</body></html>");
+
+ takeScreenshot(&page);
+}
+
+void tst_QWebPage::acceleratedWebGLScreenshotWithoutView()
+{
+ webGLScreenshotWithoutView(true);
+}
+
+void tst_QWebPage::unacceleratedWebGLScreenshotWithoutView()
+{
+ webGLScreenshotWithoutView(false);
+}
+#endif
+
+void tst_QWebPage::originatingObjectInNetworkRequests()
+{
+ 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<QWebFrame*> 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<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
+}
+
+void tst_QWebPage::networkReplyParentDidntChange()
+{
+ 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);
+}
+
+void tst_QWebPage::destroyQNAMBeforeAbortDoesntCrash()
+{
+ 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 QWebPage and can be kept alive
+ // for 60000 seconds (~16.7 hours) to then cancel its ResourceHandle.
+ m_view->stop();
+}
+
+/**
+ * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
+ *
+ * From JS we test the following conditions.
+ *
+ * OK + QString() => SUCCESS, empty string (but not null)
+ * OK + "text" => SUCCESS, "text"
+ * CANCEL + QString() => CANCEL, null string
+ * CANCEL + "text" => CANCEL, null string
+ */
+class JSPromptPage : public QWebPage {
+ Q_OBJECT
+public:
+ JSPromptPage()
+ {}
+
+ bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
+ {
+ if (msg == QLatin1String("test1")) {
+ *result = QString();
+ return true;
+ } else if (msg == QLatin1String("test2")) {
+ *result = QLatin1String("text");
+ return true;
+ } else if (msg == QLatin1String("test3")) {
+ *result = QString();
+ return false;
+ } else if (msg == QLatin1String("test4")) {
+ *result = QLatin1String("text");
+ return false;
+ }
+
+ qFatal("Unknown msg.");
+ return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
+ }
+};
+
+void tst_QWebPage::testJSPrompt()
+{
+ JSPromptPage page;
+ bool res;
+
+ // OK + QString()
+ res = page.mainFrame()->evaluateJavaScript(
+ "var retval = prompt('test1');"
+ "retval=='' && retval.length == 0;").toBool();
+ QVERIFY(res);
+
+ // OK + "text"
+ res = page.mainFrame()->evaluateJavaScript(
+ "var retval = prompt('test2');"
+ "retval=='text' && retval.length == 4;").toBool();
+ QVERIFY(res);
+
+ // Cancel + QString()
+ res = page.mainFrame()->evaluateJavaScript(
+ "var retval = prompt('test3');"
+ "retval===null;").toBool();
+ QVERIFY(res);
+
+ // Cancel + "text"
+ res = page.mainFrame()->evaluateJavaScript(
+ "var retval = prompt('test4');"
+ "retval===null;").toBool();
+ QVERIFY(res);
+}
+
+class TestModalPage : public QWebPage
+{
+ Q_OBJECT
+public:
+ TestModalPage(QObject* parent = 0) : QWebPage(parent) {
+ }
+ virtual QWebPage* createWindow(WebWindowType) {
+ QWebPage* page = new TestModalPage();
+ connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
+ return page;
+ }
+};
+
+void tst_QWebPage::showModalDialog()
+{
+ TestModalPage page;
+ page.settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
+ page.mainFrame()->setHtml(QString("<html></html>"));
+ QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
+ QCOMPARE(res, QString("This is a test"));
+}
+
+void tst_QWebPage::testStopScheduledPageRefresh()
+{
+ // Without QWebPage::StopScheduledPageRefresh
+ QWebPage page1;
+ page1.setNetworkAccessManager(new TestNetworkManager(&page1));
+ page1.mainFrame()->setHtml("<html><head>"
+ "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">"
+ "</head><body><h1>Page redirects immediately...</h1>"
+ "</body></html>");
+ QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
+ QTest::qWait(500);
+ QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html")));
+
+ // With QWebPage::StopScheduledPageRefresh
+ QWebPage page2;
+ page2.setNetworkAccessManager(new TestNetworkManager(&page2));
+ page2.mainFrame()->setHtml("<html><head>"
+ "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">"
+ "</head><body><h1>Page redirect test with 1 sec timeout...</h1>"
+ "</body></html>");
+ page2.triggerAction(QWebPage::StopScheduledPageRefresh);
+ QTest::qWait(1500);
+ QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118673", Continue);
+ QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank"));
+}
+
+void tst_QWebPage::findText()
+{
+ m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
+ m_page->triggerAction(QWebPage::SelectAll);
+ QVERIFY(!m_page->selectedText().isEmpty());
+ QVERIFY(!m_page->selectedHtml().isEmpty());
+ m_page->findText("");
+ QVERIFY(m_page->selectedText().isEmpty());
+ QVERIFY(m_page->selectedHtml().isEmpty());
+ QStringList words = (QStringList() << "foo" << "bar");
+ foreach (QString subString, words) {
+ m_page->findText(subString, QWebPage::FindWrapsAroundDocument);
+ QCOMPARE(m_page->selectedText(), subString);
+ QVERIFY(m_page->selectedHtml().contains(subString));
+ m_page->findText("");
+ QVERIFY(m_page->selectedText().isEmpty());
+ QVERIFY(m_page->selectedHtml().isEmpty());
+ }
+}
+
+static QString getMimeTypeForExtension(const QString &ext)
+{
+ QMimeType mimeType = QMimeDatabase().mimeTypeForFile(QStringLiteral("filename.") + ext.toLower(), QMimeDatabase::MatchExtension);
+ if (mimeType.isValid() && !mimeType.isDefault())
+ return mimeType.name();
+
+ return QString();
+}
+
+void tst_QWebPage::supportedContentType()
+{
+ QStringList contentTypes;
+
+ // Add supported non image types...
+ contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/"
+ << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml"
+ << "application/rss+xml" << "application/atom+xml" << "application/json";
+
+#if ENABLE_MHTML
+ contentTypes << "application/x-mimearchive";
+#endif
+
+ // Add supported image types...
+ Q_FOREACH (const QByteArray& imageType, QImageWriter::supportedImageFormats()) {
+ const QString mimeType = getMimeTypeForExtension(imageType);
+ if (!mimeType.isEmpty())
+ contentTypes << mimeType;
+ }
+
+ // Get the mime types supported by webkit...
+ const QStringList supportedContentTypes = m_page->supportedContentTypes();
+
+ Q_FOREACH (const QString& mimeType, contentTypes)
+ QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
+
+ Q_FOREACH (const QString& mimeType, contentTypes)
+ QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
+}
+
+
+void tst_QWebPage::navigatorCookieEnabled()
+{
+ m_page->networkAccessManager()->setCookieJar(0);
+ QVERIFY(!m_page->networkAccessManager()->cookieJar());
+ QVERIFY(!m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
+
+ m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
+ QVERIFY(m_page->networkAccessManager()->cookieJar());
+ QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
+}
+
+void tst_QWebPage::thirdPartyCookiePolicy()
+{
+ QWebSettings::globalSettings()->setThirdPartyCookiePolicy(QWebSettings::AlwaysBlockThirdPartyCookies);
+ m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
+ QVERIFY(m_page->networkAccessManager()->cookieJar());
+
+ // These are all first-party cookies, so should pass.
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.com"), QUrl("http://example.com")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.com"), QUrl("http://doc.example.com")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://aaa.www.example.com"), QUrl("http://doc.example.com")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://example.com"), QUrl("http://www.example.com")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.co.uk"), QUrl("http://example.co.uk")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.co.uk"), QUrl("http://doc.example.co.uk")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://aaa.www.example.co.uk"), QUrl("http://doc.example.co.uk")));
+ QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://example.co.uk"), QUrl("http://www.example.co.uk")));
+
+ // These are all third-party cookies, so should fail.
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.com"), QUrl("http://slashdot.org")));
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://example.com"), QUrl("http://anotherexample.com")));
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://anotherexample.com"), QUrl("http://example.com")));
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://www.example.co.uk"), QUrl("http://slashdot.co.uk")));
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://example.co.uk"), QUrl("http://anotherexample.co.uk")));
+ QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
+ QUrl("http://anotherexample.co.uk"), QUrl("http://example.co.uk")));
+}
+
+#ifdef Q_OS_MAC
+void tst_QWebPage::macCopyUnicodeToClipboard()
+{
+ QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ");
+ m_page->mainFrame()->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText));
+ m_page->triggerAction(QWebPage::SelectAll);
+ m_page->triggerAction(QWebPage::Copy);
+
+ QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html")));
+
+ QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />")));
+ QVERIFY(clipboardData.contains(unicodeText));
+}
+#endif
+
+void tst_QWebPage::contextMenuCopy()
+{
+ QWebView view;
+
+ view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>");
+
+ view.page()->triggerAction(QWebPage::SelectAll);
+ QVERIFY(!view.page()->selectedText().isEmpty());
+
+ QWebElement link = view.page()->mainFrame()->findFirstElement("a");
+ QPoint pos(link.geometry().center());
+ QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
+ view.page()->swallowContextMenuEvent(&event);
+ view.page()->updatePositionDependentActions(pos);
+
+ QList<QMenu*> contextMenus = view.findChildren<QMenu*>();
+ QVERIFY(!contextMenus.isEmpty());
+ QMenu* contextMenu = contextMenus.first();
+ QVERIFY(contextMenu);
+
+ QList<QAction *> list = contextMenu->actions();
+ int index = list.indexOf(view.page()->action(QWebPage::Copy));
+ QVERIFY(index != -1);
+}
+
+// https://bugs.webkit.org/show_bug.cgi?id=62139
+void tst_QWebPage::contextMenuPopulatedOnce()
+{
+ QWebView view;
+
+ view.setHtml("<input type=\"text\">");
+
+ QWebElement link = view.page()->mainFrame()->findFirstElement("input");
+ QPoint pos(link.geometry().center());
+ QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
+ view.page()->swallowContextMenuEvent(&event);
+ view.page()->updatePositionDependentActions(pos);
+
+ QList<QMenu*> contextMenus = view.findChildren<QMenu*>();
+ QVERIFY(!contextMenus.isEmpty());
+ QMenu* contextMenu = contextMenus.first();
+ QVERIFY(contextMenu);
+
+ QList<QAction *> list = contextMenu->actions();
+ QStringList entries;
+ while (!list.isEmpty()) {
+ QString entry = list.takeFirst()->text();
+ QVERIFY(!entries.contains(entry));
+ entries << entry;
+ }
+}
+
+void tst_QWebPage::deleteQWebViewTwice()
+{
+ for (int i = 0; i < 2; ++i) {
+ QMainWindow mainWindow;
+ QWebView* webView = new QWebView(&mainWindow);
+ mainWindow.setCentralWidget(webView);
+ webView->load(QUrl("qrc:///resources/frame_a.html"));
+ mainWindow.show();
+ QVERIFY(::waitForSignal(webView, SIGNAL(loadFinished(bool))));
+ }
+}
+
+class RepaintRequestedRenderer : public QObject {
+ Q_OBJECT
+public:
+ RepaintRequestedRenderer(QWebPage* page, QPainter* painter)
+ : m_page(page)
+ , m_painter(painter)
+ , m_recursionCount(0)
+ {
+ connect(m_page, SIGNAL(repaintRequested(QRect)), this, SLOT(onRepaintRequested(QRect)));
+ }
+
+Q_SIGNALS:
+ void finished();
+
+private Q_SLOTS:
+ void onRepaintRequested(const QRect& rect)
+ {
+ QCOMPARE(m_recursionCount, 0);
+
+ m_recursionCount++;
+ m_page->mainFrame()->render(m_painter, rect);
+ m_recursionCount--;
+
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ }
+
+private:
+ QWebPage* m_page;
+ QPainter* m_painter;
+ int m_recursionCount;
+};
+
+void tst_QWebPage::renderOnRepaintRequestedShouldNotRecurse()
+{
+ QSize viewportSize(720, 576);
+ QWebPage page;
+
+ QImage image(viewportSize, QImage::Format_ARGB32);
+ QPainter painter(&image);
+
+ page.setPreferredContentsSize(viewportSize);
+ page.setViewportSize(viewportSize);
+ RepaintRequestedRenderer r(&page, &painter);
+
+ page.mainFrame()->setHtml("zalan loves trunk", QUrl());
+
+ QVERIFY(::waitForSignal(&r, SIGNAL(finished())));
+}
+
+class SpyForLoadSignalsOrder : public QStateMachine {
+ Q_OBJECT
+public:
+ SpyForLoadSignalsOrder(QWebPage* page, QObject* parent = 0)
+ : QStateMachine(parent)
+ {
+ connect(page, SIGNAL(loadProgress(int)), SLOT(onLoadProgress(int)));
+
+ QState* waitingForLoadStarted = new QState(this);
+ QState* waitingForLastLoadProgress = new QState(this);
+ QState* waitingForLoadFinished = new QState(this);
+ QFinalState* final = new QFinalState(this);
+
+ waitingForLoadStarted->addTransition(page, SIGNAL(loadStarted()), waitingForLastLoadProgress);
+ waitingForLastLoadProgress->addTransition(this, SIGNAL(lastLoadProgress()), waitingForLoadFinished);
+ waitingForLoadFinished->addTransition(page, SIGNAL(loadFinished(bool)), final);
+
+ setInitialState(waitingForLoadStarted);
+ start();
+ }
+ bool isFinished() const
+ {
+ return !isRunning();
+ }
+public Q_SLOTS:
+ void onLoadProgress(int progress)
+ {
+ if (progress == 100)
+ emit lastLoadProgress();
+ }
+Q_SIGNALS:
+ void lastLoadProgress();
+};
+
+void tst_QWebPage::loadSignalsOrder_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::newRow("inline data") << QUrl("data:text/html,This is first page");
+ QTest::newRow("simple page") << QUrl("qrc:///resources/content.html");
+ QTest::newRow("frameset page") << QUrl("qrc:///resources/index.html");
+}
+
+void tst_QWebPage::loadSignalsOrder()
+{
+ QFETCH(QUrl, url);
+ QWebPage page;
+ SpyForLoadSignalsOrder loadSpy(&page);
+ waitForSignal(&loadSpy, SIGNAL(started()));
+ page.mainFrame()->load(url);
+ QTRY_VERIFY(loadSpy.isFinished());
+}
+
+void tst_QWebPage::undoActionHaveCustomText()
+{
+ m_page->mainFrame()->setHtml("<div id=test contenteditable></div>");
+ m_page->mainFrame()->evaluateJavaScript("document.getElementById('test').focus()");
+
+ m_page->mainFrame()->evaluateJavaScript("document.execCommand('insertText', true, 'Test');");
+ QString typingActionText = m_page->action(QWebPage::Undo)->text();
+
+ m_page->mainFrame()->evaluateJavaScript("document.execCommand('indent', true);");
+ QString alignActionText = m_page->action(QWebPage::Undo)->text();
+
+ QVERIFY(typingActionText != alignActionText);
+}
+
+void tst_QWebPage::openWindowDefaultSize()
+{
+ TestPage page;
+ page.settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
+ // Open a default window.
+ page.mainFrame()->evaluateJavaScript("window.open()");
+ // Open a too small window.
+ page.mainFrame()->evaluateJavaScript("window.open('', '', 'width=10,height=10')");
+
+ QTest::qWait(500);
+ // The number of popups created should be two.
+ QVERIFY(page.createdWindows.size() == 2);
+
+ QRect requestedGeometry = page.createdWindows[0]->requestedGeometry;
+ // Check default size has been requested.
+ QVERIFY(requestedGeometry.width() == 0);
+ QVERIFY(requestedGeometry.height() == 0);
+
+ requestedGeometry = page.createdWindows[1]->requestedGeometry;
+ // Check minimum size has been requested.
+ QVERIFY(requestedGeometry.width() == 100);
+ QVERIFY(requestedGeometry.height() == 100);
+}
+
+void tst_QWebPage::cssMediaTypeGlobalSetting()
+{
+ QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>");
+ QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
+
+ QWebSettings::globalSettings()->setCSSMediaType("tv");
+ // Clear page specific setting to read from global setting
+ m_view->page()->settings()->setCSSMediaType(QString());
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('tv').matches == true").toBool());
+ QVERIFY(QWebSettings::globalSettings()->cssMediaType() == "tv");
+
+ QWebSettings::globalSettings()->setCSSMediaType("handheld");
+ // Clear page specific setting to read from global setting
+ m_view->page()->settings()->setCSSMediaType(QString());
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('handheld').matches == true").toBool());
+ QVERIFY(QWebSettings::globalSettings()->cssMediaType() == "handheld");
+
+ QWebSettings::globalSettings()->setCSSMediaType("screen");
+ // Clear page specific setting to read from global setting
+ m_view->page()->settings()->setCSSMediaType(QString());
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 3);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('screen').matches == true").toBool());
+ QVERIFY(QWebSettings::globalSettings()->cssMediaType() == "screen");
+}
+
+void tst_QWebPage::cssMediaTypePageSetting()
+{
+ QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>");
+ QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
+
+ m_view->page()->settings()->setCSSMediaType("tv");
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('tv').matches == true").toBool());
+ QVERIFY(m_view->page()->settings()->cssMediaType() == "tv");
+
+ m_view->page()->settings()->setCSSMediaType("handheld");
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 2);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('handheld').matches == true").toBool());
+ QVERIFY(m_view->page()->settings()->cssMediaType() == "handheld");
+
+ m_view->page()->settings()->setCSSMediaType("screen");
+ m_view->setHtml(testHtml);
+ QTRY_COMPARE(loadSpy.count(), 3);
+ QVERIFY(m_view->page()->mainFrame()->evaluateJavaScript("window.matchMedia('screen').matches == true").toBool());
+ QVERIFY(m_view->page()->settings()->cssMediaType() == "screen");
+}
+
+QTEST_MAIN(tst_QWebPage)
+#include "tst_qwebpage.moc"
diff --git a/tests/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/widgets/qwebenginepage/tst_qwebenginepage.qrc
new file mode 100644
index 000000000..994d71b43
--- /dev/null
+++ b/tests/widgets/qwebenginepage/tst_qwebenginepage.qrc
@@ -0,0 +1,14 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resources/index.html</file>
+ <file>resources/frame_a.html</file>
+ <file>resources/frame_c.html</file>
+ <file>resources/iframe.html</file>
+ <file>resources/iframe2.html</file>
+ <file>resources/iframe3.html</file>
+ <file>resources/framedindex.html</file>
+ <file>resources/content.html</file>
+ <file>resources/script.html</file>
+ <file>resources/user.css</file>
+</qresource>
+</RCC>
diff --git a/tests/widgets/qwebengineview/qwebengineview.pro b/tests/widgets/qwebengineview/qwebengineview.pro
new file mode 100644
index 000000000..ff6c49628
--- /dev/null
+++ b/tests/widgets/qwebengineview/qwebengineview.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
diff --git a/tests/widgets/qwebengineview/resources/frame_a.html b/tests/widgets/qwebengineview/resources/frame_a.html
new file mode 100644
index 000000000..9ff68f13a
--- /dev/null
+++ b/tests/widgets/qwebengineview/resources/frame_a.html
@@ -0,0 +1,2 @@
+<a href="http://google.com" target="frame_b"><img src="" width=100 height=100 alt="Google"></a>
+<a href="http://yahoo.com" target="frame_b"><img src="" width=100 height=100 alt="Yahoo"></a>
diff --git a/tests/widgets/qwebengineview/resources/index.html b/tests/widgets/qwebengineview/resources/index.html
new file mode 100644
index 000000000..c53ad09a7
--- /dev/null
+++ b/tests/widgets/qwebengineview/resources/index.html
@@ -0,0 +1,4 @@
+<frameset cols="25%,75%">
+ <frame src="frame_a.html" name="frame_a">
+ <frame src="frame_b.html" name="frame_b">
+</frameset>
diff --git a/tests/widgets/qwebengineview/resources/input_types.html b/tests/widgets/qwebengineview/resources/input_types.html
new file mode 100644
index 000000000..2e893afae
--- /dev/null
+++ b/tests/widgets/qwebengineview/resources/input_types.html
@@ -0,0 +1,9 @@
+<html><body>
+<input type='text' maxlength='20' style='position: absolute; left: 10px; top: 0px; height: 50px; width: 100px;'/><br>
+<input type='password' style='position: absolute; left: 10px; top: 50px; height: 50px; width: 100px;'/><br>
+<input type='tel' style='position: absolute; left: 10px; top: 100px; height: 50px; width: 100px;'/><br>
+<input type='number' style='position: absolute; left: 10px; top: 150px; height: 50px; width: 100px;'/><br>
+<input type='email' style='position: absolute; left: 10px; top: 200px; height: 50px; width: 100px;'/><br>
+<input type='url' style='position: absolute; left: 10px; top: 250px; height: 50px; width: 100px;'/><br>
+<textarea style='position: absolute; left: 10px; top: 310px; height: 50px; width: 100px;' rows="2" cols="20">blah blah blah blah</textarea><br>
+</body></html>
diff --git a/tests/widgets/qwebengineview/resources/scrolltest_page.html b/tests/widgets/qwebengineview/resources/scrolltest_page.html
new file mode 100644
index 000000000..18fcbbebe
--- /dev/null
+++ b/tests/widgets/qwebengineview/resources/scrolltest_page.html
@@ -0,0 +1,6 @@
+<html>
+<head><title>Scrolling test</title></head>
+<body>
+ <div style="width: 1000px; height: 1000px; background-color: green"/>
+</body>
+</html>
diff --git a/tests/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/widgets/qwebengineview/tst_qwebengineview.cpp
new file mode 100644
index 000000000..9d08f677d
--- /dev/null
+++ b/tests/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -0,0 +1,522 @@
+/*
+ Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2009 Torch Mobile Inc.
+ Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
+
+ 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 <qtest.h>
+#include "../util.h"
+
+#include <qpainter.h>
+#include <qwebview.h>
+#include <qwebpage.h>
+#include <qnetworkrequest.h>
+#include <qdiriterator.h>
+#include <qwebelement.h>
+#include <qwebframe.h>
+
+#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
+ QVERIFY(actual == expect);
+
+class tst_QWebView : public QObject
+{
+ Q_OBJECT
+
+public Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+private Q_SLOTS:
+ void renderingAfterMaxAndBack();
+ void renderHints();
+ void getWebKitVersion();
+
+ void reusePage_data();
+ void reusePage();
+ void microFocusCoordinates();
+ void focusInputTypes();
+ void horizontalScrollbarTest();
+
+ void crashTests();
+#if !(defined(WTF_USE_QT_MOBILE_THEME) && WTF_USE_QT_MOBILE_THEME)
+ void setPalette_data();
+ void setPalette();
+#endif
+};
+
+// This will be called before the first test function is executed.
+// It is only called once.
+void tst_QWebView::initTestCase()
+{
+}
+
+// This will be called after the last test function is executed.
+// It is only called once.
+void tst_QWebView::cleanupTestCase()
+{
+}
+
+// This will be called before each test function is executed.
+void tst_QWebView::init()
+{
+}
+
+// This will be called after every test function.
+void tst_QWebView::cleanup()
+{
+}
+
+void tst_QWebView::renderHints()
+{
+ QWebView webView;
+
+ // default is only text antialiasing + smooth pixmap transform
+ QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
+ QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
+
+ webView.setRenderHint(QPainter::Antialiasing, true);
+ QVERIFY(webView.renderHints() & QPainter::Antialiasing);
+ QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
+
+ webView.setRenderHint(QPainter::Antialiasing, false);
+ QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
+ QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
+
+ webView.setRenderHint(QPainter::SmoothPixmapTransform, true);
+ QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
+ QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
+ QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
+
+ webView.setRenderHint(QPainter::SmoothPixmapTransform, false);
+ QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
+ QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform));
+ QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
+}
+
+void tst_QWebView::getWebKitVersion()
+{
+ QVERIFY(qWebKitVersion().toDouble() > 0);
+}
+
+void tst_QWebView::reusePage_data()
+{
+ QTest::addColumn<QString>("html");
+ QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
+ QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
+ QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode=\"transparent\"></embed></body></html>");
+}
+
+void tst_QWebView::reusePage()
+{
+ 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);
+
+ QFETCH(QString, html);
+ QWebView* view1 = new QWebView;
+ QPointer<QWebPage> page = new QWebPage;
+ view1->setPage(page.data());
+ page.data()->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
+ QWebFrame* mainFrame = page.data()->mainFrame();
+ mainFrame->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
+ waitForSignal(view1, SIGNAL(loadFinished(bool)), 2000);
+ }
+
+ view1->show();
+ QTest::qWaitForWindowExposed(view1);
+ delete view1;
+ QVERIFY(page != 0); // deleting view must not have deleted the page, since it's not a child of view
+
+ QWebView *view2 = new QWebView;
+ view2->setPage(page.data());
+ view2->show(); // in Windowless mode, you should still be able to see the plugin here
+ QTest::qWaitForWindowExposed(view2);
+ delete view2;
+
+ delete page.data(); // must not crash
+
+ QDir::setCurrent(QApplication::applicationDirPath());
+}
+
+// Class used in crashTests
+class WebViewCrashTest : public QObject {
+ Q_OBJECT
+ QWebView* m_view;
+public:
+ bool m_executed;
+
+
+ WebViewCrashTest(QWebView* view)
+ : m_view(view)
+ , m_executed(false)
+ {
+ view->connect(view, SIGNAL(loadProgress(int)), this, SLOT(loading(int)));
+ }
+
+private Q_SLOTS:
+ void loading(int progress)
+ {
+ if (progress >= 20 && progress < 90) {
+ QVERIFY(!m_executed);
+ m_view->stop();
+ m_executed = true;
+ }
+ }
+};
+
+
+// Should not crash.
+void tst_QWebView::crashTests()
+{
+ // Test if loading can be stopped in loadProgress handler without crash.
+ // Test page should have frames.
+ QWebView view;
+ WebViewCrashTest tester(&view);
+ QUrl url("qrc:///resources/index.html");
+ view.load(url);
+ QTRY_VERIFY(tester.m_executed); // If fail it means that the test wasn't executed.
+}
+
+void tst_QWebView::microFocusCoordinates()
+{
+ QWebPage* page = new QWebPage;
+ QWebView* webView = new QWebView;
+ webView->setPage( page );
+
+ page->mainFrame()->setHtml("<html><body>" \
+ "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \
+ "<canvas id='canvas1' width='500' height='500'></canvas>" \
+ "<input type='password'/><br>" \
+ "<canvas id='canvas2' width='500' height='500'></canvas>" \
+ "</body></html>");
+
+ page->mainFrame()->setFocus();
+
+ QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
+ QVERIFY(initialMicroFocus.isValid());
+
+ page->mainFrame()->scroll(0,50);
+
+ QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
+ QVERIFY(currentMicroFocus.isValid());
+
+ QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-50)), currentMicroFocus.toRect());
+}
+
+void tst_QWebView::focusInputTypes()
+{
+ QWebView webView;
+ webView.show();
+ QTest::qWaitForWindowExposed(&webView);
+
+ QUrl url("qrc:///resources/input_types.html");
+ QWebFrame* const mainFrame = webView.page()->mainFrame();
+ mainFrame->load(url);
+ mainFrame->setFocus();
+
+ QVERIFY(waitForSignal(&webView, SIGNAL(loadFinished(bool))));
+
+ // 'text' type
+ QWebElement inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'password' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'tel' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=tel]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDialableCharactersOnly);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'number' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=number]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDigitsOnly);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'email' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=email]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhEmailCharactersOnly);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'url' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=url]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhUrlCharactersOnly);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'password' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'text' type
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'password' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // 'text area' field
+ inputElement = mainFrame->documentElement().findFirst(QLatin1String("textarea"));
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
+ QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
+ QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
+}
+
+void tst_QWebView::horizontalScrollbarTest()
+{
+ QWebView webView;
+ webView.resize(600, 600);
+ webView.show();
+ QTest::qWaitForWindowExposed(&webView);
+
+ QUrl url("qrc:///resources/scrolltest_page.html");
+ QWebFrame* const mainFrame = webView.page()->mainFrame();
+ mainFrame->load(url);
+ mainFrame->setFocus();
+
+ QVERIFY(waitForSignal(&webView, SIGNAL(loadFinished(bool))));
+
+ QVERIFY(webView.page()->mainFrame()->scrollPosition() == QPoint(0, 0));
+
+ // Note: The test below assumes that the layout direction is Qt::LeftToRight.
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, QPoint(550, 595));
+ QVERIFY(webView.page()->mainFrame()->scrollPosition().x() > 0);
+
+ // Note: The test below assumes that the layout direction is Qt::LeftToRight.
+ QTest::mouseClick(&webView, Qt::LeftButton, 0, QPoint(20, 595));
+ QVERIFY(webView.page()->mainFrame()->scrollPosition() == QPoint(0, 0));
+}
+
+
+#if !(defined(WTF_USE_QT_MOBILE_THEME) && WTF_USE_QT_MOBILE_THEME)
+void tst_QWebView::setPalette_data()
+{
+ QTest::addColumn<bool>("active");
+ QTest::addColumn<bool>("background");
+ QTest::newRow("activeBG") << true << true;
+ QTest::newRow("activeFG") << true << false;
+ QTest::newRow("inactiveBG") << false << true;
+ QTest::newRow("inactiveFG") << false << false;
+}
+
+// Render a QWebView to a QImage twice, each time with a different palette set,
+// verify that images rendered are not the same, confirming WebCore usage of
+// custom palette on selections.
+void tst_QWebView::setPalette()
+{
+ QString html = "<html><head></head>"
+ "<body>"
+ "Some text here"
+ "</body>"
+ "</html>";
+
+ QFETCH(bool, active);
+ QFETCH(bool, background);
+
+ QWidget* activeView = 0;
+
+ // Use controlView to manage active/inactive state of test views by raising
+ // or lowering their position in the window stack.
+ QWebView controlView;
+ controlView.setHtml(html);
+
+ QWebView view1;
+
+ QPalette palette1;
+ QBrush brush1(Qt::red);
+ brush1.setStyle(Qt::SolidPattern);
+ if (active && background) {
+ // Rendered image must have red background on an active QWebView.
+ palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1);
+ } else if (active && !background) {
+ // Rendered image must have red foreground on an active QWebView.
+ palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1);
+ } else if (!active && background) {
+ // Rendered image must have red background on an inactive QWebView.
+ palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1);
+ } else if (!active && !background) {
+ // Rendered image must have red foreground on an inactive QWebView.
+ palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1);
+ }
+
+ view1.setPalette(palette1);
+ view1.setHtml(html);
+ view1.page()->setViewportSize(view1.page()->currentFrame()->contentsSize());
+ view1.show();
+
+ QTest::qWaitForWindowExposed(&view1);
+
+ if (!active) {
+ controlView.show();
+ QTest::qWaitForWindowExposed(&controlView);
+ activeView = &controlView;
+ controlView.activateWindow();
+ } else {
+ view1.activateWindow();
+ activeView = &view1;
+ }
+
+ QTRY_COMPARE(QApplication::activeWindow(), activeView);
+
+ view1.page()->triggerAction(QWebPage::SelectAll);
+
+ QImage img1(view1.page()->viewportSize(), QImage::Format_ARGB32);
+ QPainter painter1(&img1);
+ view1.page()->currentFrame()->render(&painter1);
+ painter1.end();
+ view1.close();
+ controlView.close();
+
+ QWebView view2;
+
+ QPalette palette2;
+ QBrush brush2(Qt::blue);
+ brush2.setStyle(Qt::SolidPattern);
+ if (active && background) {
+ // Rendered image must have blue background on an active QWebView.
+ palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2);
+ } else if (active && !background) {
+ // Rendered image must have blue foreground on an active QWebView.
+ palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2);
+ } else if (!active && background) {
+ // Rendered image must have blue background on an inactive QWebView.
+ palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2);
+ } else if (!active && !background) {
+ // Rendered image must have blue foreground on an inactive QWebView.
+ palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2);
+ }
+
+ view2.setPalette(palette2);
+ view2.setHtml(html);
+ view2.page()->setViewportSize(view2.page()->currentFrame()->contentsSize());
+ view2.show();
+
+ QTest::qWaitForWindowExposed(&view2);
+
+ if (!active) {
+ controlView.show();
+ QTest::qWaitForWindowExposed(&controlView);
+ activeView = &controlView;
+ controlView.activateWindow();
+ } else {
+ view2.activateWindow();
+ activeView = &view2;
+ }
+
+ QTRY_COMPARE(QApplication::activeWindow(), activeView);
+
+ view2.page()->triggerAction(QWebPage::SelectAll);
+
+ QImage img2(view2.page()->viewportSize(), QImage::Format_ARGB32);
+ QPainter painter2(&img2);
+ view2.page()->currentFrame()->render(&painter2);
+ painter2.end();
+
+ view2.close();
+ controlView.close();
+
+ QVERIFY(img1 != img2);
+}
+#endif
+
+void tst_QWebView::renderingAfterMaxAndBack()
+{
+ QUrl url = QUrl("data:text/html,<html><head></head>"
+ "<body width=1024 height=768 bgcolor=red>"
+ "</body>"
+ "</html>");
+
+ QWebView view;
+ view.page()->mainFrame()->load(url);
+ QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool))));
+ view.show();
+
+ view.page()->settings()->setMaximumPagesInCache(3);
+
+ QTest::qWaitForWindowExposed(&view);
+
+ QPixmap reference(view.page()->viewportSize());
+ reference.fill(Qt::red);
+
+ QPixmap image(view.page()->viewportSize());
+ QPainter painter(&image);
+ view.page()->currentFrame()->render(&painter);
+
+ QCOMPARE(image, reference);
+
+ QUrl url2 = QUrl("data:text/html,<html><head></head>"
+ "<body width=1024 height=768 bgcolor=blue>"
+ "</body>"
+ "</html>");
+ view.page()->mainFrame()->load(url2);
+
+ QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool))));
+
+ view.showMaximized();
+
+ QTest::qWaitForWindowExposed(&view);
+
+ QPixmap reference2(view.page()->viewportSize());
+ reference2.fill(Qt::blue);
+
+ QPixmap image2(view.page()->viewportSize());
+ QPainter painter2(&image2);
+ view.page()->currentFrame()->render(&painter2);
+
+ QCOMPARE(image2, reference2);
+
+ view.back();
+
+ QPixmap reference3(view.page()->viewportSize());
+ reference3.fill(Qt::red);
+ QPixmap image3(view.page()->viewportSize());
+ QPainter painter3(&image3);
+ view.page()->currentFrame()->render(&painter3);
+
+ QCOMPARE(image3, reference3);
+}
+
+QTEST_MAIN(tst_QWebView)
+#include "tst_qwebview.moc"
diff --git a/tests/widgets/qwebengineview/tst_qwebengineview.qrc b/tests/widgets/qwebengineview/tst_qwebengineview.qrc
new file mode 100644
index 000000000..6685a8086
--- /dev/null
+++ b/tests/widgets/qwebengineview/tst_qwebengineview.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resources/index.html</file>
+ <file>resources/frame_a.html</file>
+ <file>resources/input_types.html</file>
+ <file>resources/scrolltest_page.html</file>
+</qresource>
+</RCC>
diff --git a/tests/widgets/resources/image2.png b/tests/widgets/resources/image2.png
new file mode 100644
index 000000000..8d703640c
--- /dev/null
+++ b/tests/widgets/resources/image2.png
Binary files differ
diff --git a/tests/widgets/resources/test.swf b/tests/widgets/resources/test.swf
new file mode 100644
index 000000000..895298271
--- /dev/null
+++ b/tests/widgets/resources/test.swf
Binary files differ
diff --git a/tests/widgets/tests.pri b/tests/widgets/tests.pri
new file mode 100644
index 000000000..b48806286
--- /dev/null
+++ b/tests/widgets/tests.pri
@@ -0,0 +1,22 @@
+TEMPLATE = app
+
+VPATH += $$_PRO_FILE_PWD_
+TARGET = tst_$$TARGET
+
+# Load mobilityconfig if Qt Mobility is available
+load(mobilityconfig, true)
+contains(MOBILITY_CONFIG, multimedia) {
+ # This define is used by tests depending on Qt Multimedia
+ DEFINES -= WTF_USE_QT_MULTIMEDIA=0
+ DEFINES += WTF_USE_QT_MULTIMEDIA=1
+}
+
+SOURCES += $${TARGET}.cpp
+INCLUDEPATH += \
+ $$PWD \
+ $$PWD/../Api
+
+QT += testlib network webkitwidgets widgets
+
+# This define is used by some tests to look up resources in the source tree
+DEFINES += TESTS_SOURCE_DIR=\\\"$$PWD/\\\"
diff --git a/tests/widgets/util.h b/tests/widgets/util.h
new file mode 100644
index 000000000..4925aa4c7
--- /dev/null
+++ b/tests/widgets/util.h
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 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.
+*/
+// Functions and macros that really need to be in QTestLib
+
+#if 0
+#pragma qt_no_master_include
+#endif
+
+#include <QEventLoop>
+#include <QSignalSpy>
+#include <QTimer>
+
+#if !defined(TESTS_SOURCE_DIR)
+#define TESTS_SOURCE_DIR ""
+#endif
+
+/**
+ * Starts an event loop that runs until the given signal is received.
+ * Optionally the event loop
+ * can return earlier on a timeout.
+ *
+ * \return \p true if the requested signal was received
+ * \p false on timeout
+ */
+static inline bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000)
+{
+ QEventLoop loop;
+ QObject::connect(obj, signal, &loop, SLOT(quit()));
+ QTimer timer;
+ QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
+ if (timeout > 0) {
+ QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
+ timer.setSingleShot(true);
+ timer.start(timeout);
+ }
+ loop.exec();
+ return timeoutSpy.isEmpty();
+}
+
+/**
+ * Just like QSignalSpy but facilitates sync and async
+ * signal emission. For example if you want to verify that
+ * page->foo() emitted a signal, it could be that the
+ * implementation decides to emit the signal asynchronously
+ * - in which case we want to spin a local event loop until
+ * emission - or that the call to foo() emits it right away.
+ */
+class SignalBarrier : private QSignalSpy
+{
+public:
+ SignalBarrier(const QObject* obj, const char* aSignal)
+ : QSignalSpy(obj, aSignal)
+ { }
+
+ bool ensureSignalEmitted()
+ {
+ bool result = count() > 0;
+ if (!result)
+ result = wait();
+ clear();
+ return result;
+ }
+};
+
+#define W_QSKIP(a, b) QSKIP(a)