1 /*
2 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2009 Torch Mobile Inc.
4 Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21
22 #include <qtest.h>
23 #include "../util.h"
24
25 #include <qpainter.h>
26 #include <qwebview.h>
27 #include <qwebpage.h>
28 #include <qnetworkrequest.h>
29 #include <qdiriterator.h>
30 #include <qwebkitversion.h>
31 #include <qwebelement.h>
32 #include <qwebframe.h>
33
34 #ifdef Q_OS_SYMBIAN
35 #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
36 QVERIFY(actual & Qt::ImhNoAutoUppercase); \
37 QVERIFY(actual & Qt::ImhNoPredictiveText); \
38 QVERIFY(actual & expect);
39 #else
40 #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
41 QVERIFY(actual == expect);
42 #endif
43
44 class tst_QWebView : public QObject
45 {
46 Q_OBJECT
47
48 public slots:
49 void initTestCase();
50 void cleanupTestCase();
51 void init();
52 void cleanup();
53
54 private slots:
55 void renderingAfterMaxAndBack();
56 void renderHints();
57 void getWebKitVersion();
58
59 void reusePage_data();
60 void reusePage();
61 void microFocusCoordinates();
62 void focusInputTypes();
63
64 void crashTests();
65
66 void setPalette_data();
67 void setPalette();
68 };
69
70 // This will be called before the first test function is executed.
71 // It is only called once.
initTestCase()72 void tst_QWebView::initTestCase()
73 {
74 }
75
76 // This will be called after the last test function is executed.
77 // It is only called once.
cleanupTestCase()78 void tst_QWebView::cleanupTestCase()
79 {
80 }
81
82 // This will be called before each test function is executed.
init()83 void tst_QWebView::init()
84 {
85 }
86
87 // This will be called after every test function.
cleanup()88 void tst_QWebView::cleanup()
89 {
90 }
91
renderHints()92 void tst_QWebView::renderHints()
93 {
94 QWebView webView;
95
96 // default is only text antialiasing + smooth pixmap transform
97 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
98 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
99 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
100 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
101
102 webView.setRenderHint(QPainter::Antialiasing, true);
103 QVERIFY(webView.renderHints() & QPainter::Antialiasing);
104 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
105 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
106 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
107
108 webView.setRenderHint(QPainter::Antialiasing, false);
109 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
110 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
111 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
112 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
113
114 webView.setRenderHint(QPainter::SmoothPixmapTransform, true);
115 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
116 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
117 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
118 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
119
120 webView.setRenderHint(QPainter::SmoothPixmapTransform, false);
121 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
122 QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform));
123 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
124 }
125
getWebKitVersion()126 void tst_QWebView::getWebKitVersion()
127 {
128 QVERIFY(qWebKitVersion().toDouble() > 0);
129 }
130
reusePage_data()131 void tst_QWebView::reusePage_data()
132 {
133 QTest::addColumn<QString>("html");
134 QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
135 QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
136 QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode=\"transparent\"></embed></body></html>");
137 }
138
reusePage()139 void tst_QWebView::reusePage()
140 {
141 if (!QDir(TESTS_SOURCE_DIR).exists())
142 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
143
144 QDir::setCurrent(TESTS_SOURCE_DIR);
145
146 QFETCH(QString, html);
147 QWebView* view1 = new QWebView;
148 QPointer<QWebPage> page = new QWebPage;
149 view1->setPage(page);
150 page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
151 QWebFrame* mainFrame = page->mainFrame();
152 mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
153 if (html.contains("</embed>")) {
154 // some reasonable time for the PluginStream to feed test.swf to flash and start painting
155 waitForSignal(view1, SIGNAL(loadFinished(bool)), 2000);
156 }
157
158 view1->show();
159 QTest::qWaitForWindowShown(view1);
160 delete view1;
161 QVERIFY(page != 0); // deleting view must not have deleted the page, since it's not a child of view
162
163 QWebView *view2 = new QWebView;
164 view2->setPage(page);
165 view2->show(); // in Windowless mode, you should still be able to see the plugin here
166 QTest::qWaitForWindowShown(view2);
167 delete view2;
168
169 delete page; // must not crash
170
171 QDir::setCurrent(QApplication::applicationDirPath());
172 }
173
174 // Class used in crashTests
175 class WebViewCrashTest : public QObject {
176 Q_OBJECT
177 QWebView* m_view;
178 public:
179 bool m_executed;
180
181
WebViewCrashTest(QWebView * view)182 WebViewCrashTest(QWebView* view)
183 : m_view(view)
184 , m_executed(false)
185 {
186 view->connect(view, SIGNAL(loadProgress(int)), this, SLOT(loading(int)));
187 }
188
189 private slots:
loading(int progress)190 void loading(int progress)
191 {
192 if (progress >= 20 && progress < 90) {
193 QVERIFY(!m_executed);
194 m_view->stop();
195 m_executed = true;
196 }
197 }
198 };
199
200
201 // Should not crash.
crashTests()202 void tst_QWebView::crashTests()
203 {
204 // Test if loading can be stopped in loadProgress handler without crash.
205 // Test page should have frames.
206 QWebView view;
207 WebViewCrashTest tester(&view);
208 QUrl url("qrc:///resources/index.html");
209 view.load(url);
210 QTRY_VERIFY(tester.m_executed); // If fail it means that the test wasn't executed.
211 }
212
microFocusCoordinates()213 void tst_QWebView::microFocusCoordinates()
214 {
215 QWebPage* page = new QWebPage;
216 QWebView* webView = new QWebView;
217 webView->setPage( page );
218
219 page->mainFrame()->setHtml("<html><body>" \
220 "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \
221 "<canvas id='canvas1' width='500' height='500'></canvas>" \
222 "<input type='password'/><br>" \
223 "<canvas id='canvas2' width='500' height='500'></canvas>" \
224 "</body></html>");
225
226 page->mainFrame()->setFocus();
227
228 QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
229 QVERIFY(initialMicroFocus.isValid());
230
231 page->mainFrame()->scroll(0,50);
232
233 QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
234 QVERIFY(currentMicroFocus.isValid());
235
236 QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-50)), currentMicroFocus.toRect());
237 }
238
focusInputTypes()239 void tst_QWebView::focusInputTypes()
240 {
241 QWebView webView;
242 webView.show();
243 QTest::qWaitForWindowShown(&webView);
244
245 QUrl url("qrc:///resources/input_types.html");
246 QWebFrame* const mainFrame = webView.page()->mainFrame();
247 mainFrame->load(url);
248 mainFrame->setFocus();
249
250 QVERIFY(waitForSignal(&webView, SIGNAL(loadFinished(bool))));
251
252 // 'text' type
253 QWebElement inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]"));
254 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
255 #if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN)
256 QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase);
257 QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText);
258 #else
259 QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
260 #endif
261 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
262
263 // 'password' field
264 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
265 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
266 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
267 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
268
269 // 'tel' field
270 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=tel]"));
271 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
272 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDialableCharactersOnly);
273 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
274
275 // 'number' field
276 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=number]"));
277 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
278 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDigitsOnly);
279 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
280
281 // 'email' field
282 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=email]"));
283 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
284 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhEmailCharactersOnly);
285 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
286
287 // 'url' field
288 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=url]"));
289 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
290 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhUrlCharactersOnly);
291 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
292
293 // 'password' field
294 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
295 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
296 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
297 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
298
299 // 'text' type
300 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]"));
301 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
302 #if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN)
303 QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase);
304 QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText);
305 #else
306 QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
307 #endif
308 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
309
310 // 'password' field
311 inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]"));
312 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
313 VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText);
314 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
315
316 // 'text area' field
317 inputElement = mainFrame->documentElement().findFirst(QLatin1String("textarea"));
318 QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center());
319 #if defined(Q_OS_SYMBIAN)
320 QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase);
321 QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText);
322 #else
323 QVERIFY(webView.inputMethodHints() == Qt::ImhNone);
324 #endif
325 QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled));
326 }
327
setPalette_data()328 void tst_QWebView::setPalette_data()
329 {
330 QTest::addColumn<bool>("active");
331 QTest::addColumn<bool>("background");
332 QTest::newRow("activeBG") << true << true;
333 QTest::newRow("activeFG") << true << false;
334 QTest::newRow("inactiveBG") << false << true;
335 QTest::newRow("inactiveFG") << false << false;
336 }
337
338 // Render a QWebView to a QImage twice, each time with a different palette set,
339 // verify that images rendered are not the same, confirming WebCore usage of
340 // custom palette on selections.
setPalette()341 void tst_QWebView::setPalette()
342 {
343 QString html = "<html><head></head>"
344 "<body>"
345 "Some text here"
346 "</body>"
347 "</html>";
348
349 QFETCH(bool, active);
350 QFETCH(bool, background);
351
352 QWidget* activeView = 0;
353
354 // Use controlView to manage active/inactive state of test views by raising
355 // or lowering their position in the window stack.
356 QWebView controlView;
357 controlView.setHtml(html);
358
359 QWebView view1;
360
361 QPalette palette1;
362 QBrush brush1(Qt::red);
363 brush1.setStyle(Qt::SolidPattern);
364 if (active && background) {
365 // Rendered image must have red background on an active QWebView.
366 palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1);
367 } else if (active && !background) {
368 // Rendered image must have red foreground on an active QWebView.
369 palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1);
370 } else if (!active && background) {
371 // Rendered image must have red background on an inactive QWebView.
372 palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1);
373 } else if (!active && !background) {
374 // Rendered image must have red foreground on an inactive QWebView.
375 palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1);
376 }
377
378 view1.setPalette(palette1);
379 view1.setHtml(html);
380 view1.page()->setViewportSize(view1.page()->currentFrame()->contentsSize());
381 view1.show();
382
383 QTest::qWaitForWindowShown(&view1);
384
385 if (!active) {
386 controlView.show();
387 QTest::qWaitForWindowShown(&controlView);
388 activeView = &controlView;
389 controlView.activateWindow();
390 } else {
391 view1.activateWindow();
392 activeView = &view1;
393 }
394
395 QTRY_COMPARE(QApplication::activeWindow(), activeView);
396
397 view1.page()->triggerAction(QWebPage::SelectAll);
398
399 QImage img1(view1.page()->viewportSize(), QImage::Format_ARGB32);
400 QPainter painter1(&img1);
401 view1.page()->currentFrame()->render(&painter1);
402 painter1.end();
403 view1.close();
404 controlView.close();
405
406 QWebView view2;
407
408 QPalette palette2;
409 QBrush brush2(Qt::blue);
410 brush2.setStyle(Qt::SolidPattern);
411 if (active && background) {
412 // Rendered image must have blue background on an active QWebView.
413 palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2);
414 } else if (active && !background) {
415 // Rendered image must have blue foreground on an active QWebView.
416 palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2);
417 } else if (!active && background) {
418 // Rendered image must have blue background on an inactive QWebView.
419 palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2);
420 } else if (!active && !background) {
421 // Rendered image must have blue foreground on an inactive QWebView.
422 palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2);
423 }
424
425 view2.setPalette(palette2);
426 view2.setHtml(html);
427 view2.page()->setViewportSize(view2.page()->currentFrame()->contentsSize());
428 view2.show();
429
430 QTest::qWaitForWindowShown(&view2);
431
432 if (!active) {
433 controlView.show();
434 QTest::qWaitForWindowShown(&controlView);
435 activeView = &controlView;
436 controlView.activateWindow();
437 } else {
438 view2.activateWindow();
439 activeView = &view2;
440 }
441
442 QTRY_COMPARE(QApplication::activeWindow(), activeView);
443
444 view2.page()->triggerAction(QWebPage::SelectAll);
445
446 QImage img2(view2.page()->viewportSize(), QImage::Format_ARGB32);
447 QPainter painter2(&img2);
448 view2.page()->currentFrame()->render(&painter2);
449 painter2.end();
450
451 view2.close();
452 controlView.close();
453
454 QVERIFY(img1 != img2);
455 }
456
renderingAfterMaxAndBack()457 void tst_QWebView::renderingAfterMaxAndBack()
458 {
459 QUrl url = QUrl("data:text/html,<html><head></head>"
460 "<body width=1024 height=768 bgcolor=red>"
461 "</body>"
462 "</html>");
463
464 QWebView view;
465 view.page()->mainFrame()->load(url);
466 QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool))));
467 view.show();
468
469 view.page()->settings()->setMaximumPagesInCache(3);
470
471 QTest::qWaitForWindowShown(&view);
472
473 QPixmap reference(view.page()->viewportSize());
474 reference.fill(Qt::red);
475
476 QPixmap image(view.page()->viewportSize());
477 QPainter painter(&image);
478 view.page()->currentFrame()->render(&painter);
479
480 QCOMPARE(image, reference);
481
482 QUrl url2 = QUrl("data:text/html,<html><head></head>"
483 "<body width=1024 height=768 bgcolor=blue>"
484 "</body>"
485 "</html>");
486 view.page()->mainFrame()->load(url2);
487
488 QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool))));
489
490 view.showMaximized();
491
492 QTest::qWaitForWindowShown(&view);
493
494 QPixmap reference2(view.page()->viewportSize());
495 reference2.fill(Qt::blue);
496
497 QPixmap image2(view.page()->viewportSize());
498 QPainter painter2(&image2);
499 view.page()->currentFrame()->render(&painter2);
500
501 QCOMPARE(image2, reference2);
502
503 view.back();
504
505 QPixmap reference3(view.page()->viewportSize());
506 reference3.fill(Qt::red);
507 QPixmap image3(view.page()->viewportSize());
508 QPainter painter3(&image3);
509 view.page()->currentFrame()->render(&painter3);
510
511 QCOMPARE(image3, reference3);
512 }
513
514 QTEST_MAIN(tst_QWebView)
515 #include "tst_qwebview.moc"
516
517