2 Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.com>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
21 #include <QtTest/QtTest>
22 #include <QGraphicsSceneMouseEvent>
23 #include <QGraphicsView>
24 #include <QStyleOptionGraphicsItem>
25 #include <qgraphicswebview.h>
27 #include <qwebframe.h>
29 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
33 class tst_QGraphicsWebView : public QObject
38 void qgraphicswebview();
39 void crashOnViewlessWebPages();
40 void microFocusCoordinates();
41 void focusInputTypes();
42 void crashOnSetScaleBeforeSetUrl();
43 void widgetsRenderingThroughCache();
44 void windowResizeEvent();
46 #if !(defined(WTF_USE_QT_MOBILE_THEME) && WTF_USE_QT_MOBILE_THEME)
47 void setPalette_data();
51 #if defined(WTF_USE_TILED_BACKING_STORE) && WTF_USE_TILED_BACKING_STORE
55 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
56 void webglSoftwareFallbackVerticalOrientation();
57 void webglSoftwareFallbackHorizontalOrientation();
60 void compareCanvasToImage(const QUrl&, const QImage&);
64 void tst_QGraphicsWebView::qgraphicswebview()
66 QGraphicsWebView item;
77 item.setZoomFactor(0);
79 item.setHtml(QString());
80 item.setContent(QByteArray());
84 class WebPage : public QWebPage
89 WebPage(QObject* parent = 0): QWebPage(parent)
93 QGraphicsWebView* webView;
96 // Force a webview deletion during the load.
97 // It should not cause WebPage to crash due to
98 // it accessing invalid pageClient pointer.
105 class GraphicsWebView : public QGraphicsWebView
110 GraphicsWebView(QGraphicsItem* parent = 0): QGraphicsWebView(parent)
114 void fireMouseClick(QPointF point) {
115 QGraphicsSceneMouseEvent presEv(QEvent::GraphicsSceneMousePress);
116 presEv.setPos(point);
117 presEv.setButton(Qt::LeftButton);
118 presEv.setButtons(Qt::LeftButton);
119 QGraphicsSceneMouseEvent relEv(QEvent::GraphicsSceneMouseRelease);
121 relEv.setButton(Qt::LeftButton);
122 relEv.setButtons(Qt::LeftButton);
123 QGraphicsWebView::sceneEvent(&presEv);
124 QGraphicsWebView::sceneEvent(&relEv);
128 void tst_QGraphicsWebView::crashOnViewlessWebPages()
130 QGraphicsScene scene;
131 QGraphicsView view(&scene);
133 QGraphicsWebView* webView = new QGraphicsWebView;
134 WebPage* page = new WebPage;
135 webView->setPage(page);
136 page->webView = webView;
137 scene.addItem(webView);
139 view.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
140 view.resize(600, 480);
141 webView->resize(view.geometry().size());
143 QCoreApplication::processEvents();
146 // Resizing the page will resize and layout the empty "about:blank"
147 // page, so we first connect the signal afterward.
148 connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), page, SLOT(aborting()));
150 page->mainFrame()->load(QUrl("data:text/html,"
151 "<frameset cols=\"25%,75%\">"
152 "<frame src=\"data:text/html,foo \">"
153 "<frame src=\"data:text/html,bar\">"
156 QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool))));
160 void tst_QGraphicsWebView::crashOnSetScaleBeforeSetUrl()
162 QGraphicsWebView* webView = new QGraphicsWebView;
163 webView->setScale(2.0);
167 void tst_QGraphicsWebView::widgetsRenderingThroughCache()
169 // Widgets should be rendered the same way with and without
170 // intermediate cache (tiling for example).
171 // See bug https://bugs.webkit.org/show_bug.cgi?id=47767 where
172 // widget are rendered as disabled when caching is using.
174 QGraphicsWebView* webView = new QGraphicsWebView;
175 webView->setHtml(QLatin1String("<body style=\"background-color: white\"><input type=range></input><input type=checkbox></input><input type=radio></input><input type=file></input></body>"));
178 // Disable the scrollbars on the graphics view because QtWebKit handles scrolling and scrollbar automatically
179 view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
180 view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
182 QGraphicsScene* scene = new QGraphicsScene(&view);
183 view.setScene(scene);
184 scene->addItem(webView);
185 view.setGeometry(QRect(0, 0, 500, 500));
186 QWidget *const widget = &view;
187 QTest::qWaitForWindowShown(widget);
189 // 1. Reference without tiling.
190 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, false);
191 QPixmap referencePixmap(view.size());
192 widget->render(&referencePixmap);
195 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
196 QPixmap viewWithTiling(view.size());
197 widget->render(&viewWithTiling);
198 QApplication::processEvents();
199 widget->render(&viewWithTiling);
201 QCOMPARE(referencePixmap.toImage(), viewWithTiling.toImage());
204 #if defined(WTF_USE_TILED_BACKING_STORE) && WTF_USE_TILED_BACKING_STORE
205 void tst_QGraphicsWebView::bug57798()
207 // When content size grows from less than viewport size to more than that, tiles may need to be regenerated.
209 QGraphicsWebView* webView = new QGraphicsWebView();
210 webView->setGeometry(QRectF(0.0, 0.0, 100.0, 100.0));
211 QGraphicsView view(new QGraphicsScene());
212 view.scene()->setParent(&view);
213 view.scene()->addItem(webView);
214 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
215 QStyleOptionGraphicsItem option;
216 option.exposedRect = view.sceneRect();
217 QImage img(view.width(), view.height(),
218 QImage::Format_ARGB32_Premultiplied);
219 QPainter painter(&img);
220 // This will not paint anything as the tiles are not ready, but will trigger tile creation with size (0, 0).
221 webView->paint(&painter, &option);
222 QApplication::processEvents();
223 QUrl url("qrc:///resources/greendiv.html");
225 QVERIFY(waitForSignal(webView, SIGNAL(loadFinished(bool))));
226 // This should trigger the recreation of the tiles.
227 webView->paint(&painter, &option);
228 QApplication::processEvents();
229 painter.fillRect(option.exposedRect, Qt::red); // This is here to ensure failure if paint does not paint anything
230 webView->paint(&painter, &option);
231 QCOMPARE(img.pixel(option.exposedRect.width() / 4, option.exposedRect.height() / 4), qRgba(0, 128, 0, 255));
234 void tst_QGraphicsWebView::bug56929()
236 // When rendering from tiles sychronous layout should not be triggered
237 // and scrollbars should be in sync with the size of the document in the displayed state.
239 QGraphicsWebView* webView = new QGraphicsWebView();
240 webView->setGeometry(QRectF(0.0, 0.0, 100.0, 100.0));
241 QGraphicsView view(new QGraphicsScene());
242 view.scene()->setParent(&view);
243 view.scene()->addItem(webView);
244 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
245 QUrl url("qrc:///resources/greendiv.html");
247 QVERIFY(waitForSignal(webView, SIGNAL(loadFinished(bool))));
248 QStyleOptionGraphicsItem option;
249 option.exposedRect = webView->geometry();
250 QImage img(option.exposedRect.width(), option.exposedRect.height(), QImage::Format_ARGB32_Premultiplied);
251 QPainter painter(&img);
252 // This will not paint anything as the tiles are not ready, yet.
253 webView->paint(&painter, &option);
254 QApplication::processEvents();
255 webView->paint(&painter, &option);
256 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255));
257 painter.fillRect(option.exposedRect, Qt::black);
258 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(0, 0, 0, 255));
259 webView->page()->mainFrame()->evaluateJavaScript(QString("resizeDiv();"));
260 webView->paint(&painter, &option);
261 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255));
265 void tst_QGraphicsWebView::microFocusCoordinates()
267 QWebPage* page = new QWebPage;
268 QGraphicsWebView* webView = new QGraphicsWebView;
269 webView->setPage( page );
270 QGraphicsView* view = new QGraphicsView;
271 QGraphicsScene* scene = new QGraphicsScene(view);
272 view->setScene(scene);
273 scene->addItem(webView);
274 view->setGeometry(QRect(0,0,500,500));
276 page->mainFrame()->setHtml("<html><body>" \
277 "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \
278 "<canvas id='canvas1' width='500' height='500'></canvas>" \
279 "<input type='password'/><br>" \
280 "<canvas id='canvas2' width='500' height='500'></canvas>" \
283 page->mainFrame()->setFocus();
285 QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
286 QVERIFY(initialMicroFocus.isValid());
288 page->mainFrame()->scroll(0,300);
290 QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
291 QVERIFY(currentMicroFocus.isValid());
293 QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-300)), currentMicroFocus.toRect());
298 void tst_QGraphicsWebView::focusInputTypes()
300 QWebPage* page = new QWebPage;
301 GraphicsWebView* webView = new GraphicsWebView;
302 webView->setPage( page );
303 QGraphicsView* view = new QGraphicsView;
304 QGraphicsScene* scene = new QGraphicsScene(view);
305 view->setScene(scene);
306 scene->addItem(webView);
307 view->setGeometry(QRect(0,0,500,500));
308 QCoreApplication::processEvents();
309 QUrl url("qrc:///resources/input_types.html");
310 page->mainFrame()->load(url);
311 page->mainFrame()->setFocus();
313 QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool))));
316 webView->fireMouseClick(QPointF(20.0, 10.0));
317 QVERIFY(webView->inputMethodHints() == Qt::ImhNone);
320 webView->fireMouseClick(QPointF(20.0, 60.0));
321 QVERIFY(webView->inputMethodHints() & Qt::ImhHiddenText);
324 webView->fireMouseClick(QPointF(20.0, 110.0));
325 QVERIFY(webView->inputMethodHints() & Qt::ImhDialableCharactersOnly);
328 webView->fireMouseClick(QPointF(20.0, 160.0));
329 QVERIFY(webView->inputMethodHints() & Qt::ImhDigitsOnly);
332 webView->fireMouseClick(QPointF(20.0, 210.0));
333 QVERIFY(webView->inputMethodHints() & Qt::ImhEmailCharactersOnly);
336 webView->fireMouseClick(QPointF(20.0, 260.0));
337 QVERIFY(webView->inputMethodHints() & Qt::ImhUrlCharactersOnly);
343 #if !(defined(WTF_USE_QT_MOBILE_THEME) && WTF_USE_QT_MOBILE_THEME)
344 void tst_QGraphicsWebView::setPalette_data()
346 QTest::addColumn<bool>("active");
347 QTest::addColumn<bool>("background");
348 QTest::newRow("activeBG") << true << true;
349 QTest::newRow("activeFG") << true << false;
350 QTest::newRow("inactiveBG") << false << true;
351 QTest::newRow("inactiveFG") << false << false;
354 // Render a QGraphicsWebView to a QImage twice, each time with a different palette set,
355 // verify that images rendered are not the same, confirming WebCore usage of
356 // custom palette on selections.
357 void tst_QGraphicsWebView::setPalette()
359 QString html = "<html><head></head>"
365 QFETCH(bool, active);
366 QFETCH(bool, background);
368 QWidget* activeView = 0;
370 // Use controlView to manage active/inactive state of test views by raising
371 // or lowering their position in the window stack.
372 QGraphicsScene controlScene;
373 QGraphicsView controlView(&controlScene);
374 QGraphicsWebView controlWebView;
375 controlScene.addItem(&controlWebView);
376 controlWebView.setHtml(html);
377 controlWebView.setGeometry(QRectF(0, 0, 200, 200));
379 QGraphicsScene scene1;
380 QGraphicsView view1(&scene1);
381 view1.setSceneRect(0, 0, 300, 300);
382 QGraphicsWebView webView1;
383 webView1.setResizesToContents(true);
384 scene1.addItem(&webView1);
388 QBrush brush1(Qt::red);
389 brush1.setStyle(Qt::SolidPattern);
390 if (active && background) {
391 // Rendered image must have red background on an active QGraphicsWebView.
392 palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1);
393 } else if (active && !background) {
394 // Rendered image must have red foreground on an active QGraphicsWebView.
395 palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1);
396 } else if (!active && background) {
397 // Rendered image must have red background on an inactive QGraphicsWebView.
398 palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1);
399 } else if (!active && !background) {
400 // Rendered image must have red foreground on an inactive QGraphicsWebView.
401 palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1);
404 webView1.setHtml(html);
405 view1.resize(webView1.page()->viewportSize());
406 webView1.setPalette(palette1);
409 QVERIFY(webView1.palette() == palette1);
410 QVERIFY(webView1.page()->palette() == palette1);
412 QTest::qWaitForWindowShown(&view1);
416 QTest::qWaitForWindowShown(&controlView);
417 QApplication::setActiveWindow(&controlView);
418 activeView = &controlView;
419 controlView.activateWindow();
421 QApplication::setActiveWindow(&view1);
422 view1.activateWindow();
426 QTRY_COMPARE(QApplication::activeWindow(), activeView);
428 webView1.page()->triggerAction(QWebPage::SelectAll);
430 QImage img1(webView1.page()->viewportSize(), QImage::Format_ARGB32);
431 QPainter painter1(&img1);
432 webView1.page()->currentFrame()->render(&painter1);
437 QGraphicsScene scene2;
438 QGraphicsView view2(&scene2);
439 view2.setSceneRect(0, 0, 300, 300);
440 QGraphicsWebView webView2;
441 webView2.setResizesToContents(true);
442 scene2.addItem(&webView2);
446 QBrush brush2(Qt::blue);
447 brush2.setStyle(Qt::SolidPattern);
448 if (active && background) {
449 // Rendered image must have blue background on an active QGraphicsWebView.
450 palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2);
451 } else if (active && !background) {
452 // Rendered image must have blue foreground on an active QGraphicsWebView.
453 palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2);
454 } else if (!active && background) {
455 // Rendered image must have blue background on an inactive QGraphicsWebView.
456 palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2);
457 } else if (!active && !background) {
458 // Rendered image must have blue foreground on an inactive QGraphicsWebView.
459 palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2);
462 webView2.setHtml(html);
463 view2.resize(webView2.page()->viewportSize());
464 webView2.setPalette(palette2);
467 QTest::qWaitForWindowShown(&view2);
471 QTest::qWaitForWindowShown(&controlView);
472 QApplication::setActiveWindow(&controlView);
473 activeView = &controlView;
474 controlView.activateWindow();
476 QApplication::setActiveWindow(&view2);
477 view2.activateWindow();
481 QTRY_COMPARE(QApplication::activeWindow(), activeView);
483 webView2.page()->triggerAction(QWebPage::SelectAll);
485 QImage img2(webView2.page()->viewportSize(), QImage::Format_ARGB32);
486 QPainter painter2(&img2);
487 webView2.page()->currentFrame()->render(&painter2);
493 QVERIFY(img1 != img2);
497 void tst_QGraphicsWebView::renderHints()
499 QGraphicsWebView webView;
501 // default is only text antialiasing + smooth pixmap transform
502 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
503 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
504 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
505 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
507 webView.setRenderHint(QPainter::Antialiasing, true);
508 QVERIFY(webView.renderHints() & QPainter::Antialiasing);
509 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
510 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
511 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
513 webView.setRenderHint(QPainter::Antialiasing, false);
514 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
515 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
516 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
517 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
519 webView.setRenderHint(QPainter::SmoothPixmapTransform, true);
520 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
521 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
522 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
523 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
525 webView.setRenderHint(QPainter::SmoothPixmapTransform, false);
526 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
527 QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform));
528 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
531 class GraphicsView : public QGraphicsView {
534 QGraphicsWebView* m_webView;
537 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
538 bool compareImagesFuzzyPixelCount(const QImage& image1, const QImage& image2, qreal tolerance = 0.05)
540 if (image1.size() != image2.size())
543 unsigned diffPixelCount = 0;
544 for (int row = 0; row < image1.size().width(); ++row) {
545 for (int column = 0; column < image1.size().height(); ++column)
546 if (image1.pixel(row, column) != image2.pixel(row, column))
550 if (diffPixelCount > (image1.size().width() * image1.size().height()) * tolerance)
556 GraphicsView::GraphicsView()
558 QGraphicsScene* const scene = new QGraphicsScene(this);
561 m_webView = new QGraphicsWebView;
562 scene->addItem(m_webView);
564 m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true);
565 m_webView->setResizesToContents(true);
567 setFrameShape(QFrame::NoFrame);
568 setViewport(new QGLWidget);
571 void tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation()
573 QSize canvasSize(100, 100);
574 QImage reference(canvasSize, QImage::Format_ARGB32);
575 reference.fill(0xFF00FF00);
577 QPainter painter(&reference);
578 QPolygonF triangleUp;
579 triangleUp << QPointF(0, canvasSize.height())
580 << QPointF(canvasSize.width(), canvasSize.height())
581 << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
582 painter.setPen(Qt::NoPen);
583 painter.setBrush(Qt::red);
584 painter.drawPolygon(triangleUp);
587 compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_up.html")), reference);
590 void tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation()
592 QSize canvasSize(100, 100);
593 QImage reference(canvasSize, QImage::Format_ARGB32);
594 reference.fill(0xFF00FF00);
596 QPainter painter(&reference);
597 QPolygonF triangleUp;
598 triangleUp << QPointF(0, 0)
599 << QPointF(0, canvasSize.height())
600 << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
601 painter.setPen(Qt::NoPen);
602 painter.setBrush(Qt::red);
603 painter.drawPolygon(triangleUp);
606 compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_right.html")), reference);
609 void tst_QGraphicsWebView::compareCanvasToImage(const QUrl& url, const QImage& reference)
613 QTest::qWaitForWindowShown(&view);
615 QGraphicsWebView* const graphicsWebView = view.m_webView;
616 graphicsWebView->load(url);
617 QVERIFY(waitForSignal(graphicsWebView, SIGNAL(loadFinished(bool))));
618 { // Force a render, to create the accelerated compositing tree.
619 QPixmap pixmap(view.size());
620 QPainter painter(&pixmap);
621 view.render(&painter);
623 QApplication::syncX();
625 const QSize imageSize = reference.size();
627 QImage target(imageSize, QImage::Format_ARGB32);
628 { // Web page content.
629 QPainter painter(&target);
630 QRectF renderRect(0, 0, imageSize.width(), imageSize.height());
631 view.scene()->render(&painter, renderRect, renderRect);
633 QVERIFY(compareImagesFuzzyPixelCount(target, reference, 0.01));
637 class ResizeSpy : public QObject {
640 void receiveResize(int width, int height)
642 m_size = QSize(width, height);
658 void tst_QGraphicsWebView::windowResizeEvent()
660 QGraphicsWebView webView;
662 resizeSpy.setProperty("resizeCount", 0);
664 QString html = "<html><body><script>"
665 "function onResize() { window.resizeSpy.receiveResize(window.innerWidth, window.innerHeight); }"
666 "window.addEventListener('resize', onResize , false);"
667 "</script></body></html>";
669 webView.page()->mainFrame()->setHtml(html);
670 webView.page()->mainFrame()->addToJavaScriptWindowObject("resizeSpy",
672 webView.setGeometry(QRect(0, 0, 50, 50));
673 QVERIFY(::waitForSignal(&resizeSpy, SIGNAL(resized()), 1000));
674 QCOMPARE(resizeSpy.size(), QSize(50, 50));
676 webView.page()->setActualVisibleContentRect(QRect(10, 10, 60, 60));
677 webView.setGeometry(QRect(0, 0, 100, 100));
678 waitForSignal(&resizeSpy, SIGNAL(resized()), 1000);
680 // This will be triggered without the fix on DOMWindow::innerHeight/Width
681 QCOMPARE(resizeSpy.size(), QSize(60, 60));
684 QTEST_MAIN(tst_QGraphicsWebView)
686 #include "tst_qgraphicswebview.moc"