• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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