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