1 /*
2 Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.com>
3
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.
8
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.
13
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.
18 */
19
20 #include "../util.h"
21 #include <QtTest/QtTest>
22 #include <QGraphicsSceneMouseEvent>
23 #include <QGraphicsView>
24 #include <QStyleOptionGraphicsItem>
25 #include <qgraphicswebview.h>
26 #include <qwebpage.h>
27 #include <qwebframe.h>
28
29 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
30 #include <QGLWidget>
31 #endif
32
33 class tst_QGraphicsWebView : public QObject
34 {
35 Q_OBJECT
36
37 private slots:
38 void qgraphicswebview();
39 void crashOnViewlessWebPages();
40 void microFocusCoordinates();
41 void focusInputTypes();
42 void crashOnSetScaleBeforeSetUrl();
43 void widgetsRenderingThroughCache();
44 void setPalette_data();
45 void setPalette();
46 void renderHints();
47 #if defined(ENABLE_TILED_BACKING_STORE) && ENABLE_TILED_BACKING_STORE
48 void bug56929();
49 #endif
50 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
51 void webglSoftwareFallbackVerticalOrientation();
52 void webglSoftwareFallbackHorizontalOrientation();
53
54 private:
55 void compareCanvasToImage(const QUrl&, const QImage&);
56 #endif
57 };
58
qgraphicswebview()59 void tst_QGraphicsWebView::qgraphicswebview()
60 {
61 QGraphicsWebView item;
62 item.url();
63 item.title();
64 item.icon();
65 item.zoomFactor();
66 item.history();
67 item.settings();
68 item.page();
69 item.setPage(0);
70 item.page();
71 item.setUrl(QUrl());
72 item.setZoomFactor(0);
73 item.load(QUrl());
74 item.setHtml(QString());
75 item.setContent(QByteArray());
76 item.isModified();
77 }
78
79 class WebPage : public QWebPage
80 {
81 Q_OBJECT
82
83 public:
WebPage(QObject * parent=0)84 WebPage(QObject* parent = 0): QWebPage(parent)
85 {
86 }
87
88 QGraphicsWebView* webView;
89
90 private slots:
91 // Force a webview deletion during the load.
92 // It should not cause WebPage to crash due to
93 // it accessing invalid pageClient pointer.
aborting()94 void aborting()
95 {
96 delete webView;
97 }
98 };
99
100 class GraphicsWebView : public QGraphicsWebView
101 {
102 Q_OBJECT
103
104 public:
GraphicsWebView(QGraphicsItem * parent=0)105 GraphicsWebView(QGraphicsItem* parent = 0): QGraphicsWebView(parent)
106 {
107 }
108
fireMouseClick(QPointF point)109 void fireMouseClick(QPointF point) {
110 QGraphicsSceneMouseEvent presEv(QEvent::GraphicsSceneMousePress);
111 presEv.setPos(point);
112 presEv.setButton(Qt::LeftButton);
113 presEv.setButtons(Qt::LeftButton);
114 QGraphicsSceneMouseEvent relEv(QEvent::GraphicsSceneMouseRelease);
115 relEv.setPos(point);
116 relEv.setButton(Qt::LeftButton);
117 relEv.setButtons(Qt::LeftButton);
118 QGraphicsWebView::sceneEvent(&presEv);
119 QGraphicsWebView::sceneEvent(&relEv);
120 }
121 };
122
crashOnViewlessWebPages()123 void tst_QGraphicsWebView::crashOnViewlessWebPages()
124 {
125 QGraphicsScene scene;
126 QGraphicsView view(&scene);
127
128 QGraphicsWebView* webView = new QGraphicsWebView;
129 WebPage* page = new WebPage;
130 webView->setPage(page);
131 page->webView = webView;
132 scene.addItem(webView);
133
134 view.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
135 view.resize(600, 480);
136 webView->resize(view.geometry().size());
137
138 QCoreApplication::processEvents();
139 view.show();
140
141 // Resizing the page will resize and layout the empty "about:blank"
142 // page, so we first connect the signal afterward.
143 connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), page, SLOT(aborting()));
144
145 page->mainFrame()->load(QUrl("data:text/html,"
146 "<frameset cols=\"25%,75%\">"
147 "<frame src=\"data:text/html,foo \">"
148 "<frame src=\"data:text/html,bar\">"
149 "</frameset>"));
150
151 QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool))));
152 delete page;
153 }
154
crashOnSetScaleBeforeSetUrl()155 void tst_QGraphicsWebView::crashOnSetScaleBeforeSetUrl()
156 {
157 QGraphicsWebView* webView = new QGraphicsWebView;
158 webView->setScale(2.0);
159 delete webView;
160 }
161
widgetsRenderingThroughCache()162 void tst_QGraphicsWebView::widgetsRenderingThroughCache()
163 {
164 // Widgets should be rendered the same way with and without
165 // intermediate cache (tiling for example).
166 // See bug https://bugs.webkit.org/show_bug.cgi?id=47767 where
167 // widget are rendered as disabled when caching is using.
168
169 QGraphicsWebView* webView = new QGraphicsWebView;
170 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>"));
171
172 QGraphicsView view;
173 view.show();
174 QGraphicsScene* scene = new QGraphicsScene(&view);
175 view.setScene(scene);
176 scene->addItem(webView);
177 view.setGeometry(QRect(0, 0, 500, 500));
178 QWidget *const widget = &view;
179 QTest::qWaitForWindowShown(widget);
180
181 // 1. Reference without tiling.
182 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, false);
183 QPixmap referencePixmap(view.size());
184 widget->render(&referencePixmap);
185
186 // 2. With tiling.
187 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
188 QPixmap viewWithTiling(view.size());
189 widget->render(&viewWithTiling);
190 QApplication::processEvents();
191 viewWithTiling.fill();
192 widget->render(&viewWithTiling);
193
194 QCOMPARE(referencePixmap.toImage(), viewWithTiling.toImage());
195 }
196
197 #if defined(ENABLE_TILED_BACKING_STORE) && ENABLE_TILED_BACKING_STORE
bug56929()198 void tst_QGraphicsWebView::bug56929()
199 {
200 // When rendering from tiles sychronous layout should not be triggered
201 // and scrollbars should be in sync with the size of the document in the displayed state.
202
203 QGraphicsWebView* webView = new QGraphicsWebView();
204 webView->setGeometry(QRectF(0.0, 0.0, 100.0, 100.0));
205 QGraphicsView view(new QGraphicsScene());
206 view.scene()->setParent(&view);
207 view.scene()->addItem(webView);
208 webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
209 QUrl url("qrc:///resources/56929.html");
210 webView->load(url);
211 QVERIFY(waitForSignal(webView, SIGNAL(loadFinished(bool))));
212 QStyleOptionGraphicsItem option;
213 option.exposedRect = webView->geometry();
214 QImage img(option.exposedRect.width(), option.exposedRect.height(), QImage::Format_ARGB32_Premultiplied);
215 QPainter painter(&img);
216 // This will not paint anything as the tiles are not ready, yet.
217 webView->paint(&painter, &option);
218 QApplication::processEvents();
219 webView->paint(&painter, &option);
220 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255));
221 painter.fillRect(option.exposedRect, Qt::black);
222 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(0, 0, 0, 255));
223 webView->page()->mainFrame()->evaluateJavaScript(QString("resizeDiv();"));
224 webView->paint(&painter, &option);
225 QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255));
226 }
227 #endif
228
microFocusCoordinates()229 void tst_QGraphicsWebView::microFocusCoordinates()
230 {
231 QWebPage* page = new QWebPage;
232 QGraphicsWebView* webView = new QGraphicsWebView;
233 webView->setPage( page );
234 QGraphicsView* view = new QGraphicsView;
235 QGraphicsScene* scene = new QGraphicsScene(view);
236 view->setScene(scene);
237 scene->addItem(webView);
238 view->setGeometry(QRect(0,0,500,500));
239
240 page->mainFrame()->setHtml("<html><body>" \
241 "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \
242 "<canvas id='canvas1' width='500' height='500'></canvas>" \
243 "<input type='password'/><br>" \
244 "<canvas id='canvas2' width='500' height='500'></canvas>" \
245 "</body></html>");
246
247 page->mainFrame()->setFocus();
248
249 QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
250 QVERIFY(initialMicroFocus.isValid());
251
252 page->mainFrame()->scroll(0,300);
253
254 QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus);
255 QVERIFY(currentMicroFocus.isValid());
256
257 QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-300)), currentMicroFocus.toRect());
258
259 delete view;
260 }
261
focusInputTypes()262 void tst_QGraphicsWebView::focusInputTypes()
263 {
264 QWebPage* page = new QWebPage;
265 GraphicsWebView* webView = new GraphicsWebView;
266 webView->setPage( page );
267 QGraphicsView* view = new QGraphicsView;
268 QGraphicsScene* scene = new QGraphicsScene(view);
269 view->setScene(scene);
270 scene->addItem(webView);
271 view->setGeometry(QRect(0,0,500,500));
272 QCoreApplication::processEvents();
273 QUrl url("qrc:///resources/input_types.html");
274 page->mainFrame()->load(url);
275 page->mainFrame()->setFocus();
276
277 QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool))));
278
279 // 'text' type
280 webView->fireMouseClick(QPointF(20.0, 10.0));
281 #if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN)
282 QVERIFY(webView->inputMethodHints() & Qt::ImhNoAutoUppercase);
283 QVERIFY(webView->inputMethodHints() & Qt::ImhNoPredictiveText);
284 #else
285 QVERIFY(webView->inputMethodHints() == Qt::ImhNone);
286 #endif
287
288 // 'password' field
289 webView->fireMouseClick(QPointF(20.0, 60.0));
290 QVERIFY(webView->inputMethodHints() & Qt::ImhHiddenText);
291
292 // 'tel' field
293 webView->fireMouseClick(QPointF(20.0, 110.0));
294 QVERIFY(webView->inputMethodHints() & Qt::ImhDialableCharactersOnly);
295
296 // 'number' field
297 webView->fireMouseClick(QPointF(20.0, 160.0));
298 QVERIFY(webView->inputMethodHints() & Qt::ImhDigitsOnly);
299
300 // 'email' field
301 webView->fireMouseClick(QPointF(20.0, 210.0));
302 QVERIFY(webView->inputMethodHints() & Qt::ImhEmailCharactersOnly);
303
304 // 'url' field
305 webView->fireMouseClick(QPointF(20.0, 260.0));
306 QVERIFY(webView->inputMethodHints() & Qt::ImhUrlCharactersOnly);
307
308 delete webView;
309 delete view;
310 }
311
setPalette_data()312 void tst_QGraphicsWebView::setPalette_data()
313 {
314 QTest::addColumn<bool>("active");
315 QTest::addColumn<bool>("background");
316 QTest::newRow("activeBG") << true << true;
317 QTest::newRow("activeFG") << true << false;
318 QTest::newRow("inactiveBG") << false << true;
319 QTest::newRow("inactiveFG") << false << false;
320 }
321
322 // Render a QGraphicsWebView to a QImage twice, each time with a different palette set,
323 // verify that images rendered are not the same, confirming WebCore usage of
324 // custom palette on selections.
setPalette()325 void tst_QGraphicsWebView::setPalette()
326 {
327 QString html = "<html><head></head>"
328 "<body>"
329 "Some text here"
330 "</body>"
331 "</html>";
332
333 QFETCH(bool, active);
334 QFETCH(bool, background);
335
336 QWidget* activeView = 0;
337
338 // Use controlView to manage active/inactive state of test views by raising
339 // or lowering their position in the window stack.
340 QGraphicsScene controlScene;
341 QGraphicsView controlView(&controlScene);
342 QGraphicsWebView controlWebView;
343 controlScene.addItem(&controlWebView);
344 controlWebView.setHtml(html);
345 controlWebView.setGeometry(QRectF(0, 0, 200, 200));
346
347 QGraphicsScene scene1;
348 QGraphicsView view1(&scene1);
349 view1.setSceneRect(0, 0, 300, 300);
350 QGraphicsWebView webView1;
351 webView1.setResizesToContents(true);
352 scene1.addItem(&webView1);
353 webView1.setFocus();
354
355 QPalette palette1;
356 QBrush brush1(Qt::red);
357 brush1.setStyle(Qt::SolidPattern);
358 if (active && background) {
359 // Rendered image must have red background on an active QGraphicsWebView.
360 palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1);
361 } else if (active && !background) {
362 // Rendered image must have red foreground on an active QGraphicsWebView.
363 palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1);
364 } else if (!active && background) {
365 // Rendered image must have red background on an inactive QGraphicsWebView.
366 palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1);
367 } else if (!active && !background) {
368 // Rendered image must have red foreground on an inactive QGraphicsWebView.
369 palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1);
370 }
371
372 webView1.setHtml(html);
373 view1.resize(webView1.page()->viewportSize());
374 webView1.setPalette(palette1);
375 view1.show();
376
377 QVERIFY(webView1.palette() == palette1);
378 QVERIFY(webView1.page()->palette() == palette1);
379
380 QTest::qWaitForWindowShown(&view1);
381
382 if (!active) {
383 controlView.show();
384 QTest::qWaitForWindowShown(&controlView);
385 activeView = &controlView;
386 controlView.activateWindow();
387 } else {
388 view1.activateWindow();
389 activeView = &view1;
390 }
391
392 QTRY_COMPARE(QApplication::activeWindow(), activeView);
393
394 webView1.page()->triggerAction(QWebPage::SelectAll);
395
396 QImage img1(webView1.page()->viewportSize(), QImage::Format_ARGB32);
397 QPainter painter1(&img1);
398 webView1.page()->currentFrame()->render(&painter1);
399 painter1.end();
400 view1.close();
401 controlView.close();
402
403 QGraphicsScene scene2;
404 QGraphicsView view2(&scene2);
405 view2.setSceneRect(0, 0, 300, 300);
406 QGraphicsWebView webView2;
407 webView2.setResizesToContents(true);
408 scene2.addItem(&webView2);
409 webView2.setFocus();
410
411 QPalette palette2;
412 QBrush brush2(Qt::blue);
413 brush2.setStyle(Qt::SolidPattern);
414 if (active && background) {
415 // Rendered image must have blue background on an active QGraphicsWebView.
416 palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2);
417 } else if (active && !background) {
418 // Rendered image must have blue foreground on an active QGraphicsWebView.
419 palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2);
420 } else if (!active && background) {
421 // Rendered image must have blue background on an inactive QGraphicsWebView.
422 palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2);
423 } else if (!active && !background) {
424 // Rendered image must have blue foreground on an inactive QGraphicsWebView.
425 palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2);
426 }
427
428 webView2.setHtml(html);
429 view2.resize(webView2.page()->viewportSize());
430 webView2.setPalette(palette2);
431 view2.show();
432
433 QTest::qWaitForWindowShown(&view2);
434
435 if (!active) {
436 controlView.show();
437 QTest::qWaitForWindowShown(&controlView);
438 activeView = &controlView;
439 controlView.activateWindow();
440 } else {
441 view2.activateWindow();
442 activeView = &view2;
443 }
444
445 QTRY_COMPARE(QApplication::activeWindow(), activeView);
446
447 webView2.page()->triggerAction(QWebPage::SelectAll);
448
449 QImage img2(webView2.page()->viewportSize(), QImage::Format_ARGB32);
450 QPainter painter2(&img2);
451 webView2.page()->currentFrame()->render(&painter2);
452 painter2.end();
453
454 view2.close();
455 controlView.close();
456
457 QVERIFY(img1 != img2);
458 }
459
renderHints()460 void tst_QGraphicsWebView::renderHints()
461 {
462 QGraphicsWebView webView;
463
464 // default is only text antialiasing + smooth pixmap transform
465 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
466 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
467 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
468 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
469
470 webView.setRenderHint(QPainter::Antialiasing, true);
471 QVERIFY(webView.renderHints() & QPainter::Antialiasing);
472 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
473 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
474 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
475
476 webView.setRenderHint(QPainter::Antialiasing, false);
477 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
478 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
479 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
480 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
481
482 webView.setRenderHint(QPainter::SmoothPixmapTransform, true);
483 QVERIFY(!(webView.renderHints() & QPainter::Antialiasing));
484 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
485 QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform);
486 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
487
488 webView.setRenderHint(QPainter::SmoothPixmapTransform, false);
489 QVERIFY(webView.renderHints() & QPainter::TextAntialiasing);
490 QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform));
491 QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
492 }
493
494 class GraphicsView : public QGraphicsView {
495 public:
496 GraphicsView();
497 QGraphicsWebView* m_webView;
498 };
499
500 #if defined(ENABLE_WEBGL) && ENABLE_WEBGL
compareImagesFuzzyPixelCount(const QImage & image1,const QImage & image2,qreal tolerance=0.05)501 bool compareImagesFuzzyPixelCount(const QImage& image1, const QImage& image2, qreal tolerance = 0.05)
502 {
503 if (image1.size() != image2.size())
504 return false;
505
506 unsigned diffPixelCount = 0;
507 for (int row = 0; row < image1.size().width(); ++row) {
508 for (int column = 0; column < image1.size().height(); ++column)
509 if (image1.pixel(row, column) != image2.pixel(row, column))
510 ++diffPixelCount;
511 }
512
513 if (diffPixelCount > (image1.size().width() * image1.size().height()) * tolerance)
514 return false;
515
516 return true;
517 }
518
GraphicsView()519 GraphicsView::GraphicsView()
520 {
521 QGraphicsScene* const scene = new QGraphicsScene(this);
522 setScene(scene);
523
524 m_webView = new QGraphicsWebView;
525 scene->addItem(m_webView);
526
527 m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true);
528 m_webView->setResizesToContents(true);
529
530 setFrameShape(QFrame::NoFrame);
531 setViewport(new QGLWidget);
532 }
533
webglSoftwareFallbackVerticalOrientation()534 void tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation()
535 {
536 QSize canvasSize(100, 100);
537 QImage reference(canvasSize, QImage::Format_ARGB32);
538 reference.fill(0xFF00FF00);
539 { // Reference.
540 QPainter painter(&reference);
541 QPolygonF triangleUp;
542 triangleUp << QPointF(0, canvasSize.height())
543 << QPointF(canvasSize.width(), canvasSize.height())
544 << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
545 painter.setPen(Qt::NoPen);
546 painter.setBrush(Qt::red);
547 painter.drawPolygon(triangleUp);
548 }
549
550 compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_up.html")), reference);
551 }
552
webglSoftwareFallbackHorizontalOrientation()553 void tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation()
554 {
555 QSize canvasSize(100, 100);
556 QImage reference(canvasSize, QImage::Format_ARGB32);
557 reference.fill(0xFF00FF00);
558 { // Reference.
559 QPainter painter(&reference);
560 QPolygonF triangleUp;
561 triangleUp << QPointF(0, 0)
562 << QPointF(0, canvasSize.height())
563 << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
564 painter.setPen(Qt::NoPen);
565 painter.setBrush(Qt::red);
566 painter.drawPolygon(triangleUp);
567 }
568
569 compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_right.html")), reference);
570 }
571
compareCanvasToImage(const QUrl & url,const QImage & reference)572 void tst_QGraphicsWebView::compareCanvasToImage(const QUrl& url, const QImage& reference)
573 {
574 GraphicsView view;
575 view.show();
576 QTest::qWaitForWindowShown(&view);
577
578 QGraphicsWebView* const graphicsWebView = view.m_webView;
579 graphicsWebView->load(url);
580 QVERIFY(waitForSignal(graphicsWebView, SIGNAL(loadFinished(bool))));
581 { // Force a render, to create the accelerated compositing tree.
582 QPixmap pixmap(view.size());
583 QPainter painter(&pixmap);
584 view.render(&painter);
585 }
586 QApplication::syncX();
587
588 const QSize imageSize = reference.size();
589
590 QImage target(imageSize, QImage::Format_ARGB32);
591 { // Web page content.
592 QPainter painter(&target);
593 QRectF renderRect(0, 0, imageSize.width(), imageSize.height());
594 view.scene()->render(&painter, renderRect, renderRect);
595 }
596 QVERIFY(compareImagesFuzzyPixelCount(target, reference, 0.01));
597 }
598 #endif
599
600 QTEST_MAIN(tst_QGraphicsWebView)
601
602 #include "tst_qgraphicswebview.moc"
603