2008-09-11 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebKit / qt / tests / qwebpage / tst_qwebpage.cpp
1 /*
2     Copyright (C) 2008 Trolltech ASA
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19
20
21 #include <QtTest/QtTest>
22
23 #include <qwebpage.h>
24 #include <qwidget.h>
25 #include <qwebview.h>
26 #include <qwebframe.h>
27 #include <qwebhistory.h>
28 #include <qnetworkrequest.h>
29 #include <QDebug>
30 #include <QMenu>
31
32 // Will try to wait for the condition while allowing event processing
33 #define QTRY_COMPARE(__expr, __expected) \
34     do { \
35         const int __step = 50; \
36         const int __timeout = 5000; \
37         if ((__expr) != (__expected)) { \
38             QTest::qWait(0); \
39         } \
40         for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
41             QTest::qWait(__step); \
42         } \
43         QCOMPARE(__expr, __expected); \
44     } while(0)
45
46 //TESTED_CLASS=
47 //TESTED_FILES=
48
49 // Task 160192
50 /**
51  * Starts an event loop that runs until the given signal is received.
52  Optionally the event loop
53  * can return earlier on a timeout.
54  *
55  * \return \p true if the requested signal was received
56  *         \p false on timeout
57  */
58 static bool waitForSignal(QObject* obj, const char* signal, int timeout = 0)
59 {
60     QEventLoop loop;
61     QObject::connect(obj, signal, &loop, SLOT(quit()));
62     QTimer timer;
63     QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
64     if (timeout > 0) {
65         QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
66         timer.setSingleShot(true);
67         timer.start(timeout);
68     }
69     loop.exec();
70     return timeoutSpy.isEmpty();
71 }
72
73 class tst_QWebPage : public QObject
74 {
75     Q_OBJECT
76
77 public:
78     tst_QWebPage();
79     virtual ~tst_QWebPage();
80
81 public slots:
82     void init();
83     void cleanup();
84
85 private slots:
86     void acceptNavigationRequest();
87     void loadFinished();
88     void acceptNavigationRequestWithNewWindow();
89     void userStyleSheet();
90     void modified();
91     void contextMenuCrash();
92
93 private:
94
95
96 private:
97     QWebView* m_view;
98     QWebPage* m_page;
99 };
100
101 tst_QWebPage::tst_QWebPage()
102 {
103 }
104
105 tst_QWebPage::~tst_QWebPage()
106 {
107 }
108
109 void tst_QWebPage::init()
110 {
111     m_view = new QWebView();
112     m_page = m_view->page();
113 }
114
115 void tst_QWebPage::cleanup()
116 {
117     delete m_view;
118 }
119
120 class NavigationRequestOverride : public QWebPage
121 {
122 public:
123     NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
124
125     bool m_acceptNavigationRequest;
126 protected:
127     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
128         Q_UNUSED(frame);
129         Q_UNUSED(request);
130         Q_UNUSED(type);
131
132         return m_acceptNavigationRequest;
133     }
134 };
135
136 void tst_QWebPage::acceptNavigationRequest()
137 {
138     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
139
140     NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
141     m_view->setPage(newPage);
142
143     m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
144                             "<input type='text'><input type='submit'></form></body></html>"), QUrl());
145     QTRY_COMPARE(loadSpy.count(), 1);
146
147     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
148
149     newPage->m_acceptNavigationRequest = true;
150     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
151     QTRY_COMPARE(loadSpy.count(), 2);
152
153     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
154
155     // Restore default page
156     m_view->setPage(0);
157 }
158
159
160 void tst_QWebPage::loadFinished()
161 {
162     QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
163     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
164
165     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
166                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
167                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
168     QTRY_COMPARE(spyLoadFinished.count(), 1);
169
170     QTest::qWait(3000);
171
172     QVERIFY(spyLoadStarted.count() > 1);
173     QVERIFY(spyLoadFinished.count() > 1);
174
175     spyLoadFinished.clear();
176
177     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
178                             "foo \"><frame src=\"data:text/html,bar\"></frameset>"), QUrl());
179     QTRY_COMPARE(spyLoadFinished.count(), 1);
180     QCOMPARE(spyLoadFinished.count(), 1);
181 }
182
183 class TestPage : public QWebPage
184 {
185 public:
186     TestPage(QObject* parent = 0) : QWebPage(parent) {}
187
188     struct Navigation {
189         QPointer<QWebFrame> frame;
190         QNetworkRequest request;
191         NavigationType type;
192     };
193
194     QList<Navigation> navigations;
195     QList<QWebPage*> createdWindows;
196
197     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
198         Navigation n;
199         n.frame = frame;
200         n.request = request;
201         n.type = type;
202         navigations.append(n);
203         return true;
204     }
205
206     virtual QWebPage* createWindow(WebWindowType type) {
207         QWebPage* page = new TestPage(this);
208         createdWindows.append(page);
209         return page;
210     }
211 };
212
213 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
214 {
215     TestPage* page = new TestPage(m_view);
216     page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
217     m_page = page;
218     m_view->setPage(m_page);
219
220     m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
221     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
222
223     QFocusEvent fe(QEvent::FocusIn);
224     m_page->event(&fe);
225
226     QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
227
228     QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
229     m_page->event(&keyEnter);
230
231     QCOMPARE(page->navigations.count(), 2);
232
233     TestPage::Navigation n = page->navigations.at(1);
234     QVERIFY(n.frame.isNull());
235     QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
236     QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
237
238     QCOMPARE(page->createdWindows.count(), 1);
239 }
240
241 class TestNetworkManager : public QNetworkAccessManager
242 {
243 public:
244     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
245
246     QList<QUrl> requestedUrls;
247
248 protected:
249     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
250         requestedUrls.append(request.url());
251         return QNetworkAccessManager::createRequest(op, request, outgoingData);
252     }
253 };
254
255 void tst_QWebPage::userStyleSheet()
256 {
257     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
258     m_page->setNetworkAccessManager(networkManager);
259     networkManager->requestedUrls.clear();
260
261     m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css,p { background-image: url('http://does.not/exist.png');}"));
262     m_view->setHtml("<p>hello world</p>");
263     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
264
265     QVERIFY(networkManager->requestedUrls.count() >= 2);
266     QCOMPARE(networkManager->requestedUrls.at(0), QUrl("data:text/css,p { background-image: url('http://does.not/exist.png');}"));
267     QCOMPARE(networkManager->requestedUrls.at(1), QUrl("http://does.not/exist.png"));
268 }
269
270 void tst_QWebPage::modified()
271 {
272     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
273     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
274
275     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
276     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
277
278     QVERIFY(!m_page->isModified());
279
280 //    m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
281     m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
282     m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
283
284     QVERIFY(m_page->isModified());
285
286     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
287
288     QVERIFY(!m_page->isModified());
289
290     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
291
292     QVERIFY(m_page->isModified());
293
294     QVERIFY(m_page->history()->canGoBack());
295     QVERIFY(!m_page->history()->canGoForward());
296     QCOMPARE(m_page->history()->count(), 2);
297     m_page->history()->back();
298     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
299
300     QVERIFY(!m_page->history()->canGoBack());
301     QVERIFY(m_page->history()->canGoForward());
302
303     QVERIFY(!m_page->isModified());
304 }
305
306 void tst_QWebPage::contextMenuCrash()
307 {
308     QWebView view;
309     view.setHtml("<p>test");
310     view.page()->updatePositionDependentActions(QPoint(0, 0));
311     QMenu* contextMenu = 0;
312     foreach (QObject* child, view.children()) {
313         contextMenu = qobject_cast<QMenu*>(child);
314         if (contextMenu)
315             break;
316     }
317     QVERIFY(contextMenu);
318     delete contextMenu;
319 }
320
321 QTEST_MAIN(tst_QWebPage)
322 #include "tst_qwebpage.moc"