Home | History | Annotate | Download | only in qwebview
      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 (at) 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.
     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.
     78 void tst_QWebView::cleanupTestCase()
     79 {
     80 }
     81 
     82 // This will be called before each test function is executed.
     83 void tst_QWebView::init()
     84 {
     85 }
     86 
     87 // This will be called after every test function.
     88 void tst_QWebView::cleanup()
     89 {
     90 }
     91 
     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 
    126 void tst_QWebView::getWebKitVersion()
    127 {
    128     QVERIFY(qWebKitVersion().toDouble() > 0);
    129 }
    130 
    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 
    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 
    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:
    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.
    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 
    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 
    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 
    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.
    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 
    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