blob: 13b9bd2103d69bdcd2c3ab83d9998e126d402041 [file] [log] [blame]
/*
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 <qwebpage.h>
#include <qwidget.h>
#include <qwebview.h>
#include <qwebframe.h>
#include <qwebhistory.h>
#include <qnetworkrequest.h>
#include <QDebug>
#include <QMenu>
// Will try to wait for the condition while allowing event processing
#define QTRY_COMPARE(__expr, __expected) \
do { \
const int __step = 50; \
const int __timeout = 5000; \
if ((__expr) != (__expected)) { \
QTest::qWait(0); \
} \
for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
QTest::qWait(__step); \
} \
QCOMPARE(__expr, __expected); \
} while(0)
//TESTED_CLASS=
//TESTED_FILES=
// Task 160192
/**
* 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 bool waitForSignal(QObject* obj, const char* signal, int timeout = 0)
{
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();
}
class tst_QWebPage : public QObject
{
Q_OBJECT
public:
tst_QWebPage();
virtual ~tst_QWebPage();
public slots:
void init();
void cleanup();
private slots:
void acceptNavigationRequest();
void loadFinished();
void acceptNavigationRequestWithNewWindow();
void userStyleSheet();
void modified();
void contextMenuCrash();
private:
private:
QWebView* m_view;
QWebPage* m_page;
};
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;
}
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);
}
void tst_QWebPage::loadFinished()
{
QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
m_view->setHtml(QString("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>"), QUrl());
QTRY_COMPARE(spyLoadFinished.count(), 1);
QTest::qWait(3000);
QVERIFY(spyLoadStarted.count() > 1);
QVERIFY(spyLoadFinished.count() > 1);
spyLoadFinished.clear();
m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
"foo \"><frame src=\"data:text/html,bar\"></frameset>"), QUrl());
QTRY_COMPARE(spyLoadFinished.count(), 1);
QCOMPARE(spyLoadFinished.count(), 1);
}
class TestPage : public QWebPage
{
public:
TestPage(QObject* parent = 0) : QWebPage(parent) {}
struct Navigation {
QPointer<QWebFrame> frame;
QNetworkRequest request;
NavigationType type;
};
QList<Navigation> navigations;
QList<QWebPage*> createdWindows;
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 type) {
QWebPage* page = new TestPage(this);
createdWindows.append(page);
return page;
}
};
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;
protected:
virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
requestedUrls.append(request.url());
return QNetworkAccessManager::createRequest(op, request, outgoingData);
}
};
void tst_QWebPage::userStyleSheet()
{
TestNetworkManager* networkManager = new TestNetworkManager(m_page);
m_page->setNetworkAccessManager(networkManager);
networkManager->requestedUrls.clear();
m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css,p { background-image: url('http://does.not/exist.png');}"));
m_view->setHtml("<p>hello world</p>");
QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
QVERIFY(networkManager->requestedUrls.count() >= 2);
QCOMPARE(networkManager->requestedUrls.at(0), QUrl("data:text/css,p { background-image: url('http://does.not/exist.png');}"));
QCOMPARE(networkManager->requestedUrls.at(1), QUrl("http://does.not/exist.png"));
}
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->mainFrame(), SIGNAL(aboutToUpdateHistory(QWebHistoryItem*))));
}
void tst_QWebPage::contextMenuCrash()
{
QWebView view;
view.setHtml("<p>test");
view.page()->updatePositionDependentActions(QPoint(0, 0));
QMenu* contextMenu = 0;
foreach (QObject* child, view.children()) {
contextMenu = qobject_cast<QMenu*>(child);
if (contextMenu)
break;
}
QVERIFY(contextMenu);
delete contextMenu;
}
QTEST_MAIN(tst_QWebPage)
#include "tst_qwebpage.moc"