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