1/* 2 Copyright (C) 2008,2009 Nokia Corporation and/or its subsidiary(-ies) 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 21#include <QtTest/QtTest> 22 23#include <qwebpage.h> 24#include <qwebelement.h> 25#include <qwebview.h> 26#include <qwebframe.h> 27#include <qwebhistory.h> 28#include <QAbstractItemView> 29#include <QApplication> 30#include <QComboBox> 31#include <QPaintEngine> 32#include <QPicture> 33#include <QRegExp> 34#include <QNetworkRequest> 35#include <QNetworkReply> 36#include <QTextCodec> 37#ifndef QT_NO_OPENSSL 38#include <qsslerror.h> 39#endif 40#include "../util.h" 41 42class tst_QWebFrame : public QObject 43{ 44 Q_OBJECT 45 46public: 47 bool eventFilter(QObject* watched, QEvent* event); 48 49public Q_SLOTS: 50 void init(); 51 void cleanup(); 52 53private Q_SLOTS: 54 void horizontalScrollAfterBack(); 55 void symmetricUrl(); 56 void progressSignal(); 57 void urlChange(); 58 void requestedUrl(); 59 void requestedUrlAfterSetAndLoadFailures(); 60 void javaScriptWindowObjectCleared_data(); 61 void javaScriptWindowObjectCleared(); 62 void javaScriptWindowObjectClearedOnEvaluate(); 63 void setHtml(); 64 void setHtmlWithImageResource(); 65 void setHtmlWithStylesheetResource(); 66 void setHtmlWithBaseURL(); 67 void setHtmlWithJSAlert(); 68 void ipv6HostEncoding(); 69 void metaData(); 70#if !defined(QT_NO_COMBOBOX) 71 void popupFocus(); 72#endif 73 void inputFieldFocus(); 74 void hitTestContent(); 75 void baseUrl_data(); 76 void baseUrl(); 77 void hasSetFocus(); 78 void renderGeometry(); 79 void renderHints(); 80 void scrollPosition(); 81 void scrollToAnchor(); 82 void scrollbarsOff(); 83 void evaluateWillCauseRepaint(); 84 void setContent_data(); 85 void setContent(); 86 void setCacheLoadControlAttribute(); 87 void setUrlWithPendingLoads(); 88 void setUrlWithFragment_data(); 89 void setUrlWithFragment(); 90 void setUrlToEmpty(); 91 void setUrlToInvalid(); 92 void setUrlHistory(); 93 void setUrlUsingStateObject(); 94 void setUrlSameUrl(); 95 void setUrlThenLoads_data(); 96 void setUrlThenLoads(); 97 void loadFinishedAfterNotFoundError(); 98 void loadInSignalHandlers_data(); 99 void loadInSignalHandlers(); 100 101private: 102 QWebView* m_view; 103 QWebPage* m_page; 104 QWebView* m_inputFieldsTestView; 105 int m_inputFieldTestPaintCount; 106}; 107 108bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event) 109{ 110 // used on the inputFieldFocus test 111 if (watched == m_inputFieldsTestView) { 112 if (event->type() == QEvent::Paint) 113 m_inputFieldTestPaintCount++; 114 } 115 return QObject::eventFilter(watched, event); 116} 117 118void tst_QWebFrame::init() 119{ 120 m_view = new QWebView(); 121 m_page = m_view->page(); 122} 123 124void tst_QWebFrame::cleanup() 125{ 126 delete m_view; 127} 128 129void tst_QWebFrame::symmetricUrl() 130{ 131 QVERIFY(m_view->url().isEmpty()); 132 133 QCOMPARE(m_view->history()->count(), 0); 134 135 QUrl dataUrl("data:text/html,<h1>Test"); 136 137 m_view->setUrl(dataUrl); 138 QCOMPARE(m_view->url(), dataUrl); 139 QCOMPARE(m_view->history()->count(), 0); 140 141 // loading is _not_ immediate, so the text isn't set just yet. 142 QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty()); 143 144 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 145 146 QCOMPARE(m_view->history()->count(), 1); 147 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test")); 148 149 QUrl dataUrl2("data:text/html,<h1>Test2"); 150 QUrl dataUrl3("data:text/html,<h1>Test3"); 151 152 m_view->setUrl(dataUrl2); 153 m_view->setUrl(dataUrl3); 154 155 QCOMPARE(m_view->url(), dataUrl3); 156 157 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 158 159 QCOMPARE(m_view->history()->count(), 2); 160 161 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3")); 162} 163 164void tst_QWebFrame::progressSignal() 165{ 166 QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); 167 168 QUrl dataUrl("data:text/html,<h1>Test"); 169 m_view->setUrl(dataUrl); 170 171 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 172 173 QVERIFY(progressSpy.size() >= 2); 174 175 // WebKit defines initialProgressValue as 10%, not 0% 176 QCOMPARE(progressSpy.first().first().toInt(), 10); 177 178 // But we always end at 100% 179 QCOMPARE(progressSpy.last().first().toInt(), 100); 180} 181 182void tst_QWebFrame::urlChange() 183{ 184 QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 185 186 QUrl dataUrl("data:text/html,<h1>Test"); 187 m_view->setUrl(dataUrl); 188 189 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 190 191 QCOMPARE(urlSpy.size(), 1); 192 193 QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); 194 m_view->setUrl(dataUrl2); 195 196 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 197 198 QCOMPARE(urlSpy.size(), 2); 199} 200 201class FakeReply : public QNetworkReply { 202 Q_OBJECT 203 204public: 205 static const QUrl urlFor404ErrorWithoutContents; 206 207 FakeReply(const QNetworkRequest& request, QObject* parent = 0) 208 : QNetworkReply(parent) 209 { 210 setOperation(QNetworkAccessManager::GetOperation); 211 setRequest(request); 212 setUrl(request.url()); 213 if (request.url() == QUrl("qrc:/test1.html")) { 214 setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); 215 setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); 216 QTimer::singleShot(0, this, SLOT(continueRedirect())); 217 } 218#ifndef QT_NO_OPENSSL 219 else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) { 220 setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error!")); 221 QTimer::singleShot(0, this, SLOT(continueError())); 222 } 223#endif 224 else if (request.url().host() == QLatin1String("abcdef.abcdef")) { 225 setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); 226 QTimer::singleShot(0, this, SLOT(continueError())); 227 } else if (request.url() == FakeReply::urlFor404ErrorWithoutContents) { 228 setError(QNetworkReply::ContentNotFoundError, "Not found"); 229 setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 404); 230 QTimer::singleShot(0, this, SLOT(continueError())); 231 } 232 233 open(QIODevice::ReadOnly); 234 } 235 ~FakeReply() 236 { 237 close(); 238 } 239 virtual void abort() {} 240 virtual void close() {} 241 242protected: 243 qint64 readData(char*, qint64) 244 { 245 return 0; 246 } 247 248private Q_SLOTS: 249 void continueRedirect() 250 { 251 emit metaDataChanged(); 252 emit finished(); 253 } 254 255 void continueError() 256 { 257 emit error(this->error()); 258 emit finished(); 259 } 260}; 261 262const QUrl FakeReply::urlFor404ErrorWithoutContents = QUrl("http://this.will/return-http-404-error-without-contents.html"); 263 264class FakeNetworkManager : public QNetworkAccessManager { 265 Q_OBJECT 266 267public: 268 FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } 269 270protected: 271 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) 272 { 273 QString url = request.url().toString(); 274 if (op == QNetworkAccessManager::GetOperation) { 275#ifndef QT_NO_OPENSSL 276 if (url == "qrc:/fake-ssl-error.html") { 277 FakeReply* reply = new FakeReply(request, this); 278 QList<QSslError> errors; 279 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); 280 return reply; 281 } 282#endif 283 if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/" || request.url() == FakeReply::urlFor404ErrorWithoutContents) 284 return new FakeReply(request, this); 285 } 286 287 return QNetworkAccessManager::createRequest(op, request, outgoingData); 288 } 289}; 290 291void tst_QWebFrame::requestedUrl() 292{ 293 QWebPage page; 294 QWebFrame* frame = page.mainFrame(); 295 296 // in few seconds, the image should be completely loaded 297 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 298 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 299 page.setNetworkAccessManager(networkManager); 300 301 frame->setUrl(QUrl("qrc:/test1.html")); 302 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 303 QCOMPARE(spy.count(), 1); 304 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html")); 305 QCOMPARE(frame->url(), QUrl("qrc:/test2.html")); 306 307 frame->setUrl(QUrl("qrc:/non-existent.html")); 308 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 309 QCOMPARE(spy.count(), 2); 310 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html")); 311 QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html")); 312 313 frame->setUrl(QUrl("http://abcdef.abcdef")); 314 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 315 QCOMPARE(spy.count(), 3); 316 QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/")); 317 QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/")); 318 319#ifndef QT_NO_OPENSSL 320 qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); 321 qRegisterMetaType<QNetworkReply* >("QNetworkReply*"); 322 323 QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); 324 frame->setUrl(QUrl("qrc:/fake-ssl-error.html")); 325 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 326 QCOMPARE(spy2.count(), 1); 327 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html")); 328 QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html")); 329#endif 330} 331 332void tst_QWebFrame::requestedUrlAfterSetAndLoadFailures() 333{ 334 QWebPage page; 335 QWebFrame* frame = page.mainFrame(); 336 337 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 338 339 const QUrl first("http://abcdef.abcdef/"); 340 frame->setUrl(first); 341 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 342 QCOMPARE(frame->url(), first); 343 QCOMPARE(frame->requestedUrl(), first); 344 QVERIFY(!spy.at(0).first().toBool()); 345 346 const QUrl second("http://abcdef.abcdef/another_page.html"); 347 QVERIFY(first != second); 348 349 frame->load(second); 350 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 351 QCOMPARE(frame->url(), first); 352 QCOMPARE(frame->requestedUrl(), second); 353 QVERIFY(!spy.at(1).first().toBool()); 354} 355 356void tst_QWebFrame::javaScriptWindowObjectCleared_data() 357{ 358 QTest::addColumn<QString>("html"); 359 QTest::addColumn<int>("signalCount"); 360 QTest::newRow("with <script>") << "<html><body><script>i=0</script><p>hello world</p></body></html>" << 1; 361 // NOTE: Empty scripts no longer cause this signal to be emitted. 362 QTest::newRow("with empty <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 0; 363 QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; 364} 365 366void tst_QWebFrame::javaScriptWindowObjectCleared() 367{ 368 QWebPage page; 369 QWebFrame* frame = page.mainFrame(); 370 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 371 QFETCH(QString, html); 372 frame->setHtml(html); 373 374 QFETCH(int, signalCount); 375 QCOMPARE(spy.count(), signalCount); 376} 377 378void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate() 379{ 380 QWebPage page; 381 QWebFrame* frame = page.mainFrame(); 382 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 383 frame->setHtml("<html></html>"); 384 QCOMPARE(spy.count(), 0); 385 frame->evaluateJavaScript("var a = 'a';"); 386 QCOMPARE(spy.count(), 1); 387 // no new clear for a new script: 388 frame->evaluateJavaScript("var a = 1;"); 389 QCOMPARE(spy.count(), 1); 390} 391 392void tst_QWebFrame::setHtml() 393{ 394 QString html("<html><head></head><body><p>hello world</p></body></html>"); 395 QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool))); 396 m_view->page()->mainFrame()->setHtml(html); 397 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); 398 QCOMPARE(spy.count(), 1); 399} 400 401void tst_QWebFrame::setHtmlWithImageResource() 402{ 403 // By default, only security origins of local files can load local resources. 404 // So we should specify baseUrl to be a local file in order to get a proper origin and load the local image. 405 406 QLatin1String html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>"); 407 QWebPage page; 408 QWebFrame* frame = page.mainFrame(); 409 410 frame->setHtml(html, QUrl(QLatin1String("file:///path/to/file"))); 411 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 412 413 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 414 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 415 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 416 417 // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. 418 419 frame->setHtml(html); 420 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 421 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 422 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 0); 423 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 0); 424} 425 426void tst_QWebFrame::setHtmlWithStylesheetResource() 427{ 428 // By default, only security origins of local files can load local resources. 429 // So we should specify baseUrl to be a local file in order to be able to download the local stylesheet. 430 431 const char* htmlData = 432 "<html>" 433 "<head>" 434 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" 435 "</head>" 436 "<body>" 437 "<p id='idP'>some text</p>" 438 "</body>" 439 "</html>"; 440 QLatin1String html(htmlData); 441 QWebPage page; 442 QWebFrame* frame = page.mainFrame(); 443 QWebElement webElement; 444 445 frame->setHtml(html, QUrl(QLatin1String("qrc:///file"))); 446 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 447 webElement = frame->documentElement().findFirst("p"); 448 QCOMPARE(webElement.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red")); 449 450 // Now we test the opposite: without a baseUrl as a local file, we cannot request local resources. 451 452 frame->setHtml(html, QUrl(QLatin1String("http://www.example.com/"))); 453 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 454 webElement = frame->documentElement().findFirst("p"); 455 QCOMPARE(webElement.styleProperty("color", QWebElement::CascadedStyle), QString()); 456} 457 458void tst_QWebFrame::setHtmlWithBaseURL() 459{ 460 // This tests if baseUrl is indeed affecting the relative paths from resources. 461 // As we are using a local file as baseUrl, its security origin should be able to load local resources. 462 463 if (!QDir(TESTS_SOURCE_DIR).exists()) 464 W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); 465 466 QDir::setCurrent(TESTS_SOURCE_DIR); 467 468 QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); 469 470 QWebPage page; 471 QWebFrame* frame = page.mainFrame(); 472 473 // in few seconds, the image should be completey loaded 474 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 475 476 frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); 477 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 478 QCOMPARE(spy.count(), 1); 479 480 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 481 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 482 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 483 484 // no history item has to be added. 485 QCOMPARE(m_view->page()->history()->count(), 0); 486} 487 488class MyPage : public QWebPage 489{ 490public: 491 MyPage() : QWebPage(), alerts(0) {} 492 int alerts; 493 494protected: 495 virtual void javaScriptAlert(QWebFrame*, const QString& msg) 496 { 497 alerts++; 498 QCOMPARE(msg, QString("foo")); 499 // Should not be enough to trigger deferred loading, since we've upped the HTML 500 // tokenizer delay in the Qt frameloader. See HTMLTokenizer::continueProcessing() 501 QTest::qWait(1000); 502 } 503}; 504 505void tst_QWebFrame::setHtmlWithJSAlert() 506{ 507 QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>"); 508 MyPage page; 509 m_view->setPage(&page); 510 page.mainFrame()->setHtml(html); 511 QCOMPARE(page.alerts, 1); 512 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); 513} 514 515class TestNetworkManager : public QNetworkAccessManager 516{ 517public: 518 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} 519 520 QList<QUrl> requestedUrls; 521 522protected: 523 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { 524 requestedUrls.append(request.url()); 525 QNetworkRequest redirectedRequest = request; 526 redirectedRequest.setUrl(QUrl("data:text/html,<p>hello")); 527 return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData); 528 } 529}; 530 531void tst_QWebFrame::ipv6HostEncoding() 532{ 533 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 534 m_page->setNetworkAccessManager(networkManager); 535 networkManager->requestedUrls.clear(); 536 537 QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html"); 538 m_view->setHtml("<p>Hi", baseUrl); 539 m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();" 540 "r.open('GET', 'http://[::1]/test.xml', false);" 541 "r.send(null);" 542 ); 543 QCOMPARE(networkManager->requestedUrls.count(), 1); 544 QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); 545} 546 547void tst_QWebFrame::metaData() 548{ 549 m_view->setHtml("<html>" 550 " <head>" 551 " <meta name=\"description\" content=\"Test description\">" 552 " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" 553 " </head>" 554 "</html>"); 555 556 QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData(); 557 558 QCOMPARE(metaData.count(), 2); 559 560 QCOMPARE(metaData.value("description"), QString("Test description")); 561 QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); 562 QCOMPARE(metaData.value("nonexistant"), QString()); 563 564 m_view->setHtml("<html>" 565 " <head>" 566 " <meta name=\"samekey\" content=\"FirstValue\">" 567 " <meta name=\"samekey\" content=\"SecondValue\">" 568 " </head>" 569 "</html>"); 570 571 metaData = m_view->page()->mainFrame()->metaData(); 572 573 QCOMPARE(metaData.count(), 2); 574 575 QStringList values = metaData.values("samekey"); 576 QCOMPARE(values.count(), 2); 577 578 QVERIFY(values.contains("FirstValue")); 579 QVERIFY(values.contains("SecondValue")); 580 581 QCOMPARE(metaData.value("nonexistant"), QString()); 582} 583 584#if !defined(QT_NO_COMBOBOX) 585void tst_QWebFrame::popupFocus() 586{ 587 QWebView view; 588 view.setHtml("<html>" 589 " <body>" 590 " <select name=\"select\">" 591 " <option>1</option>" 592 " <option>2</option>" 593 " </select>" 594 " <input type=\"text\"> </input>" 595 " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" 596 "This test checks whether showing and hiding a popup" 597 "takes the focus away from the webpage." 598 " </textarea>" 599 " </body>" 600 "</html>"); 601 view.resize(400, 100); 602 // Call setFocus before show to work around http://bugreports.qt.nokia.com/browse/QTBUG-14762 603 view.setFocus(); 604 view.show(); 605 QTest::qWaitForWindowExposed(&view); 606 view.activateWindow(); 607 QTRY_VERIFY(view.hasFocus()); 608 609 // open the popup by clicking. check if focus is on the popup 610 const QWebElement webCombo = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("select[name=select]")); 611 QTest::mouseClick(&view, Qt::LeftButton, 0, webCombo.geometry().center()); 612 613 QComboBox* combo = view.findChild<QComboBox*>(); 614 QVERIFY(combo != 0); 615 QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup 616 617 // hide the popup and check if focus is on the page 618 combo->hidePopup(); 619 QTRY_VERIFY(view.hasFocus()); // Focus should be back on the WebView 620} 621#endif 622 623void tst_QWebFrame::inputFieldFocus() 624{ 625 QWebView view; 626 view.setHtml("<html><body><input type=\"text\"></input></body></html>"); 627 view.resize(400, 100); 628 view.show(); 629 QTest::qWaitForWindowExposed(&view); 630 view.activateWindow(); 631 view.setFocus(); 632 QTRY_VERIFY(view.hasFocus()); 633 634 // double the flashing time, should at least blink once already 635 int delay = qApp->cursorFlashTime() * 2; 636 637 // focus the lineedit and check if it blinks 638 bool autoSipEnabled = qApp->autoSipEnabled(); 639 qApp->setAutoSipEnabled(false); 640 const QWebElement inputElement = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("input[type=text]")); 641 QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); 642 m_inputFieldsTestView = &view; 643 view.installEventFilter( this ); 644 QTest::qWait(delay); 645 QVERIFY2(m_inputFieldTestPaintCount >= 3, 646 "The input field should have a blinking caret"); 647 qApp->setAutoSipEnabled(autoSipEnabled); 648} 649 650void tst_QWebFrame::hitTestContent() 651{ 652 QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); 653 654 QWebPage page; 655 QWebFrame* frame = page.mainFrame(); 656 frame->setHtml(html); 657 page.setViewportSize(QSize(200, 0)); //no height so link is not visible 658 const QWebElement linkElement = frame->documentElement().findFirst(QLatin1String("a#link")); 659 QWebHitTestResult result = frame->hitTestContent(linkElement.geometry().center()); 660 QCOMPARE(result.linkText(), QString("link text")); 661 QWebElement link = result.linkElement(); 662 QCOMPARE(link.attribute("target"), QString("_foo")); 663} 664 665void tst_QWebFrame::baseUrl_data() 666{ 667 QTest::addColumn<QString>("html"); 668 QTest::addColumn<QUrl>("loadUrl"); 669 QTest::addColumn<QUrl>("url"); 670 QTest::addColumn<QUrl>("baseUrl"); 671 672 QTest::newRow("null") << QString() << QUrl() 673 << QUrl("about:blank") << QUrl("about:blank"); 674 675 QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") 676 << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); 677 678 QString html = "<html>" 679 "<head>" 680 "<base href=\"http://foobaz.bar/\" />" 681 "</head>" 682 "</html>"; 683 QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") 684 << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); 685} 686 687void tst_QWebFrame::baseUrl() 688{ 689 QFETCH(QString, html); 690 QFETCH(QUrl, loadUrl); 691 QFETCH(QUrl, url); 692 QFETCH(QUrl, baseUrl); 693 694 m_page->mainFrame()->setHtml(html, loadUrl); 695 QCOMPARE(m_page->mainFrame()->url(), url); 696 QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl); 697} 698 699void tst_QWebFrame::hasSetFocus() 700{ 701 QString html("<html><body><p>top</p>" \ 702 "<iframe width='80%' height='30%'/>" \ 703 "</body></html>"); 704 705 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); 706 m_page->mainFrame()->setHtml(html); 707 708 waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200); 709 QCOMPARE(loadSpy.size(), 1); 710 711 QList<QWebFrame*> children = m_page->mainFrame()->childFrames(); 712 QWebFrame* frame = children.at(0); 713 QString innerHtml("<html><body><p>another iframe</p>" \ 714 "<iframe width='80%' height='30%'/>" \ 715 "</body></html>"); 716 frame->setHtml(innerHtml); 717 718 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 719 QCOMPARE(loadSpy.size(), 2); 720 721 m_page->mainFrame()->setFocus(); 722 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 723 724 for (int i = 0; i < children.size(); ++i) { 725 children.at(i)->setFocus(); 726 QTRY_VERIFY(children.at(i)->hasFocus()); 727 QVERIFY(!m_page->mainFrame()->hasFocus()); 728 } 729 730 m_page->mainFrame()->setFocus(); 731 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 732} 733 734void tst_QWebFrame::renderGeometry() 735{ 736 QString html("<html>" \ 737 "<head><style>" \ 738 "body, iframe { margin: 0px; border: none; }" \ 739 "</style></head>" \ 740 "<body><iframe width='100px' height='100px'/></body>" \ 741 "</html>"); 742 743 QWebPage page; 744 page.mainFrame()->setHtml(html); 745 746 QList<QWebFrame*> frames = page.mainFrame()->childFrames(); 747 QWebFrame *frame = frames.at(0); 748 QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>"); 749 750 // By default, only security origins of local files can load local resources. 751 // So we should specify baseUrl to be a local file in order to get a proper origin. 752 frame->setHtml(innerHtml, QUrl("file:///path/to/file")); 753 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 754 755 QPicture picture; 756 757 QSize size = page.mainFrame()->contentsSize(); 758 page.setViewportSize(size); 759 760 // render contents layer only (the iframe is smaller than the image, so it will have scrollbars) 761 QPainter painter1(&picture); 762 frame->render(&painter1, QWebFrame::ContentsLayer); 763 painter1.end(); 764 765 QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width()); 766 QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height()); 767 768 // render everything, should be the size of the iframe 769 QPainter painter2(&picture); 770 frame->render(&painter2, QWebFrame::AllLayers); 771 painter2.end(); 772 773 QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px 774 QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px 775} 776 777 778class DummyPaintEngine: public QPaintEngine { 779public: 780 781 DummyPaintEngine() 782 : QPaintEngine(QPaintEngine::AllFeatures) 783 , renderHints(0) 784 { 785 } 786 787 bool begin(QPaintDevice*) 788 { 789 setActive(true); 790 return true; 791 } 792 793 bool end() 794 { 795 setActive(false); 796 return false; 797 } 798 799 void updateState(const QPaintEngineState& state) 800 { 801 renderHints = state.renderHints(); 802 } 803 804 void drawPath(const QPainterPath&) { } 805 void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } 806 807 QPaintEngine::Type type() const 808 { 809 return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); 810 } 811 812 QPainter::RenderHints renderHints; 813}; 814 815class DummyPaintDevice: public QPaintDevice { 816public: 817 DummyPaintDevice() 818 : QPaintDevice() 819 , m_engine(new DummyPaintEngine) 820 { 821 } 822 823 ~DummyPaintDevice() 824 { 825 delete m_engine; 826 } 827 828 QPaintEngine* paintEngine() const 829 { 830 return m_engine; 831 } 832 833 QPainter::RenderHints renderHints() const 834 { 835 return m_engine->renderHints; 836 } 837 838protected: 839 int metric(PaintDeviceMetric metric) const; 840 841private: 842 DummyPaintEngine* m_engine; 843 friend class DummyPaintEngine; 844}; 845 846 847int DummyPaintDevice::metric(PaintDeviceMetric metric) const 848{ 849 switch (metric) { 850 case PdmWidth: 851 return 400; 852 break; 853 854 case PdmHeight: 855 return 200; 856 break; 857 858 case PdmNumColors: 859 return INT_MAX; 860 break; 861 862 case PdmDepth: 863 return 32; 864 break; 865 866 default: 867 break; 868 } 869 return 0; 870} 871 872void tst_QWebFrame::renderHints() 873{ 874 QString html("<html><body><p>Hello, world!</p></body></html>"); 875 876 QWebPage page; 877 page.mainFrame()->setHtml(html); 878 page.setViewportSize(page.mainFrame()->contentsSize()); 879 880 // We will call frame->render and trap the paint engine state changes 881 // to ensure that GraphicsContext does not clobber the render hints. 882 DummyPaintDevice buffer; 883 QPainter painter(&buffer); 884 885 painter.setRenderHint(QPainter::TextAntialiasing, false); 886 page.mainFrame()->render(&painter); 887 QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); 888 QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); 889 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 890 891 painter.setRenderHint(QPainter::TextAntialiasing, true); 892 page.mainFrame()->render(&painter); 893 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 894 QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); 895 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 896 897 painter.setRenderHint(QPainter::SmoothPixmapTransform, true); 898 page.mainFrame()->render(&painter); 899 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 900 QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); 901 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 902 903 painter.setRenderHint(QPainter::HighQualityAntialiasing, true); 904 page.mainFrame()->render(&painter); 905 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 906 QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); 907 QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); 908} 909 910void tst_QWebFrame::scrollPosition() 911{ 912 // enlarged image in a small viewport, to provoke the scrollbars to appear 913 QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); 914 915 QWebPage page; 916 page.setViewportSize(QSize(200, 200)); 917 918 QWebFrame* frame = page.mainFrame(); 919 frame->setHtml(html); 920 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 921 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 922 923 // try to set the scroll offset programmatically 924 frame->setScrollPosition(QPoint(23, 29)); 925 QCOMPARE(frame->scrollPosition().x(), 23); 926 QCOMPARE(frame->scrollPosition().y(), 29); 927 928 int x = frame->evaluateJavaScript("window.scrollX").toInt(); 929 int y = frame->evaluateJavaScript("window.scrollY").toInt(); 930 QCOMPARE(x, 23); 931 QCOMPARE(y, 29); 932} 933 934void tst_QWebFrame::scrollToAnchor() 935{ 936 QWebPage page; 937 page.setViewportSize(QSize(480, 800)); 938 QWebFrame* frame = page.mainFrame(); 939 940 QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>" 941 "<p><a id=\"foo\">This</a> is an anchor</p>" 942 "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>" 943 "</body></html>"); 944 frame->setHtml(html); 945 frame->setScrollPosition(QPoint(0, 0)); 946 QCOMPARE(frame->scrollPosition().x(), 0); 947 QCOMPARE(frame->scrollPosition().y(), 0); 948 949 QWebElement fooAnchor = frame->findFirstElement("a[id=foo]"); 950 951 frame->scrollToAnchor("foo"); 952 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); 953 954 frame->scrollToAnchor("bar"); 955 frame->scrollToAnchor("foo"); 956 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); 957 958 frame->scrollToAnchor("top"); 959 QCOMPARE(frame->scrollPosition().y(), 0); 960 961 frame->scrollToAnchor("bar"); 962 frame->scrollToAnchor("notexist"); 963 QVERIFY(frame->scrollPosition().y() != 0); 964} 965 966 967void tst_QWebFrame::scrollbarsOff() 968{ 969 QWebView view; 970 QWebFrame* mainFrame = view.page()->mainFrame(); 971 972 mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 973 mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 974 975 QString html("<script>" \ 976 " function checkScrollbar() {" \ 977 " if (innerWidth === document.documentElement.offsetWidth)" \ 978 " document.getElementById('span1').innerText = 'SUCCESS';" \ 979 " else" \ 980 " document.getElementById('span1').innerText = 'FAIL';" \ 981 " }" \ 982 "</script>" \ 983 "<body>" \ 984 " <div style='margin-top:1000px ; margin-left:1000px'>" \ 985 " <a id='offscreen' href='a'>End</a>" \ 986 " </div>" \ 987 "<span id='span1'></span>" \ 988 "</body>"); 989 990 991 QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); 992 view.setHtml(html); 993 ::waitForSignal(&view, SIGNAL(loadFinished(bool)), 200); 994 QCOMPARE(loadSpy.count(), 1); 995 996 mainFrame->evaluateJavaScript("checkScrollbar();"); 997 QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS")); 998} 999 1000void tst_QWebFrame::horizontalScrollAfterBack() 1001{ 1002 QWebView view; 1003 QWebFrame* frame = view.page()->mainFrame(); 1004 QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); 1005 1006 view.page()->settings()->setMaximumPagesInCache(2); 1007 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); 1008 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); 1009 1010 view.load(QUrl("qrc:/testiframe2.html")); 1011 view.resize(200, 200); 1012 QTRY_COMPARE(loadSpy.count(), 1); 1013 QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); 1014 1015 view.load(QUrl("qrc:/testiframe.html")); 1016 QTRY_COMPARE(loadSpy.count(), 2); 1017 1018 view.page()->triggerAction(QWebPage::Back); 1019 QTRY_COMPARE(loadSpy.count(), 3); 1020 QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); 1021} 1022 1023void tst_QWebFrame::evaluateWillCauseRepaint() 1024{ 1025 QWebView view; 1026 QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" 1027 "junk</div>bottom</body></html>"); 1028 view.setHtml(html); 1029 view.show(); 1030 1031 QTest::qWaitForWindowExposed(&view); 1032 view.page()->mainFrame()->evaluateJavaScript( 1033 "document.getElementById('junk').style.display = 'none';"); 1034 1035 ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); 1036} 1037 1038void tst_QWebFrame::setContent_data() 1039{ 1040 QTest::addColumn<QString>("mimeType"); 1041 QTest::addColumn<QByteArray>("testContents"); 1042 QTest::addColumn<QString>("expected"); 1043 1044 QString str = QString::fromUtf8("ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει"); 1045 QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str; 1046 1047 QTextCodec *utf16 = QTextCodec::codecForName("UTF-16"); 1048 if (utf16) 1049 QTest::newRow("UTF-16 plain text") << "text/plain; charset=utf-16" << utf16->fromUnicode(str) << str; 1050 1051 str = QString::fromUtf8("Une chaîne de caractères à sa façon."); 1052 QTest::newRow("latin-1 plain text") << "text/plain; charset=iso-8859-1" << str.toLatin1() << str; 1053 1054 1055} 1056 1057void tst_QWebFrame::setContent() 1058{ 1059 QFETCH(QString, mimeType); 1060 QFETCH(QByteArray, testContents); 1061 QFETCH(QString, expected); 1062 m_view->setContent(testContents, mimeType); 1063 QWebFrame* mainFrame = m_view->page()->mainFrame(); 1064 QCOMPARE(expected , mainFrame->toPlainText()); 1065} 1066 1067class CacheNetworkAccessManager : public QNetworkAccessManager { 1068public: 1069 CacheNetworkAccessManager(QObject* parent = 0) 1070 : QNetworkAccessManager(parent) 1071 , m_lastCacheLoad(QNetworkRequest::PreferNetwork) 1072 { 1073 } 1074 1075 virtual QNetworkReply* createRequest(Operation, const QNetworkRequest& request, QIODevice*) 1076 { 1077 QVariant cacheLoad = request.attribute(QNetworkRequest::CacheLoadControlAttribute); 1078 if (cacheLoad.isValid()) 1079 m_lastCacheLoad = static_cast<QNetworkRequest::CacheLoadControl>(cacheLoad.toUInt()); 1080 else 1081 m_lastCacheLoad = QNetworkRequest::PreferNetwork; // default value 1082 return new FakeReply(request, this); 1083 } 1084 1085 QNetworkRequest::CacheLoadControl lastCacheLoad() const 1086 { 1087 return m_lastCacheLoad; 1088 } 1089 1090private: 1091 QNetworkRequest::CacheLoadControl m_lastCacheLoad; 1092}; 1093 1094void tst_QWebFrame::setCacheLoadControlAttribute() 1095{ 1096 QWebPage page; 1097 CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); 1098 page.setNetworkAccessManager(manager); 1099 QWebFrame* frame = page.mainFrame(); 1100 1101 QNetworkRequest request(QUrl("http://abcdef.abcdef/")); 1102 1103 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); 1104 frame->load(request); 1105 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); 1106 1107 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); 1108 frame->load(request); 1109 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); 1110 1111 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); 1112 frame->load(request); 1113 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); 1114 1115 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); 1116 frame->load(request); 1117 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); 1118} 1119 1120void tst_QWebFrame::setUrlWithPendingLoads() 1121{ 1122 QWebPage page; 1123 page.mainFrame()->setHtml("<img src='dummy:'/>"); 1124 page.mainFrame()->setUrl(QUrl("about:blank")); 1125} 1126 1127void tst_QWebFrame::setUrlWithFragment_data() 1128{ 1129 QTest::addColumn<QUrl>("previousUrl"); 1130 QTest::newRow("empty") << QUrl(); 1131 QTest::newRow("same URL no fragment") << QUrl("qrc:/test1.html"); 1132 // See comments in setUrlSameUrl about using setUrl() with the same url(). 1133 QTest::newRow("same URL with same fragment") << QUrl("qrc:/test1.html#"); 1134 QTest::newRow("same URL with different fragment") << QUrl("qrc:/test1.html#anotherFragment"); 1135 QTest::newRow("another URL") << QUrl("qrc:/test2.html"); 1136} 1137 1138// Based on bug report https://bugs.webkit.org/show_bug.cgi?id=32723 1139void tst_QWebFrame::setUrlWithFragment() 1140{ 1141 QFETCH(QUrl, previousUrl); 1142 1143 QWebPage page; 1144 QWebFrame* frame = page.mainFrame(); 1145 1146 if (!previousUrl.isEmpty()) { 1147 frame->load(previousUrl); 1148 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 1149 QCOMPARE(frame->url(), previousUrl); 1150 } 1151 1152 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 1153 const QUrl url("qrc:/test1.html#"); 1154 QVERIFY(!url.fragment().isNull()); 1155 1156 frame->setUrl(url); 1157 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 1158 1159 QCOMPARE(spy.count(), 1); 1160 QVERIFY(!frame->toPlainText().isEmpty()); 1161 QCOMPARE(frame->requestedUrl(), url); 1162 QCOMPARE(frame->url(), url); 1163} 1164 1165void tst_QWebFrame::setUrlToEmpty() 1166{ 1167 int expectedLoadFinishedCount = 0; 1168 const QUrl aboutBlank("about:blank"); 1169 const QUrl url("qrc:/test2.html"); 1170 1171 QWebPage page; 1172 QWebFrame* frame = page.mainFrame(); 1173 QCOMPARE(frame->url(), QUrl()); 1174 QCOMPARE(frame->requestedUrl(), QUrl()); 1175 QCOMPARE(frame->baseUrl(), QUrl()); 1176 1177 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 1178 1179 // Set existing url 1180 frame->setUrl(url); 1181 expectedLoadFinishedCount++; 1182 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 1183 1184 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1185 QCOMPARE(frame->url(), url); 1186 QCOMPARE(frame->requestedUrl(), url); 1187 QCOMPARE(frame->baseUrl(), url); 1188 1189 // Set empty url 1190 frame->setUrl(QUrl()); 1191 expectedLoadFinishedCount++; 1192 1193 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1194 QCOMPARE(frame->url(), aboutBlank); 1195 QCOMPARE(frame->requestedUrl(), QUrl()); 1196 QCOMPARE(frame->baseUrl(), aboutBlank); 1197 1198 // Set existing url 1199 frame->setUrl(url); 1200 expectedLoadFinishedCount++; 1201 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 1202 1203 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1204 QCOMPARE(frame->url(), url); 1205 QCOMPARE(frame->requestedUrl(), url); 1206 QCOMPARE(frame->baseUrl(), url); 1207 1208 // Load empty url 1209 frame->load(QUrl()); 1210 expectedLoadFinishedCount++; 1211 1212 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1213 QCOMPARE(frame->url(), aboutBlank); 1214 QCOMPARE(frame->requestedUrl(), QUrl()); 1215 QCOMPARE(frame->baseUrl(), aboutBlank); 1216} 1217 1218void tst_QWebFrame::setUrlToInvalid() 1219{ 1220 QWebPage page; 1221 QWebFrame* frame = page.mainFrame(); 1222 1223 const QUrl invalidUrl("http:/example.com"); 1224 QVERIFY(!invalidUrl.isEmpty()); 1225 QVERIFY(invalidUrl != QUrl()); 1226 1227 // QWebFrame will do its best to accept the URL, possible converting it to a valid equivalent URL. 1228 const QUrl validUrl("http://example.com/"); 1229 frame->setUrl(invalidUrl); 1230 QCOMPARE(frame->url(), validUrl); 1231 QCOMPARE(frame->requestedUrl(), validUrl); 1232 QCOMPARE(frame->baseUrl(), validUrl); 1233 1234 // QUrls equivalent to QUrl() will be treated as such. 1235 const QUrl aboutBlank("about:blank"); 1236 const QUrl anotherInvalidUrl("1http://bugs.webkit.org"); 1237 QVERIFY(!anotherInvalidUrl.isEmpty()); // and they are not necessarily empty. 1238 QVERIFY(!anotherInvalidUrl.isValid()); 1239 QCOMPARE(anotherInvalidUrl.toEncoded(), QUrl().toEncoded()); 1240 1241 frame->setUrl(anotherInvalidUrl); 1242 QCOMPARE(frame->url(), aboutBlank); 1243 QCOMPARE(frame->requestedUrl().toEncoded(), anotherInvalidUrl.toEncoded()); 1244 QCOMPARE(frame->baseUrl(), aboutBlank); 1245} 1246 1247void tst_QWebFrame::setUrlHistory() 1248{ 1249 const QUrl aboutBlank("about:blank"); 1250 QUrl url; 1251 int expectedLoadFinishedCount = 0; 1252 QWebFrame* frame = m_page->mainFrame(); 1253 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 1254 1255 QCOMPARE(m_page->history()->count(), 0); 1256 1257 frame->setUrl(QUrl()); 1258 expectedLoadFinishedCount++; 1259 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1260 QCOMPARE(frame->url(), aboutBlank); 1261 QCOMPARE(frame->requestedUrl(), QUrl()); 1262 QCOMPARE(m_page->history()->count(), 0); 1263 1264 url = QUrl("http://non.existant/"); 1265 frame->setUrl(url); 1266 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 1267 expectedLoadFinishedCount++; 1268 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1269 QCOMPARE(frame->url(), url); 1270 QCOMPARE(frame->requestedUrl(), url); 1271 QCOMPARE(m_page->history()->count(), 0); 1272 1273 url = QUrl("qrc:/test1.html"); 1274 frame->setUrl(url); 1275 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 1276 expectedLoadFinishedCount++; 1277 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1278 QCOMPARE(frame->url(), url); 1279 QCOMPARE(frame->requestedUrl(), url); 1280 QCOMPARE(m_page->history()->count(), 1); 1281 1282 frame->setUrl(QUrl()); 1283 expectedLoadFinishedCount++; 1284 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1285 QCOMPARE(frame->url(), aboutBlank); 1286 QCOMPARE(frame->requestedUrl(), QUrl()); 1287 QCOMPARE(m_page->history()->count(), 1); 1288 1289 // Loading same page as current in history, so history count doesn't change. 1290 url = QUrl("qrc:/test1.html"); 1291 frame->setUrl(url); 1292 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 1293 expectedLoadFinishedCount++; 1294 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1295 QCOMPARE(frame->url(), url); 1296 QCOMPARE(frame->requestedUrl(), url); 1297 QCOMPARE(m_page->history()->count(), 1); 1298 1299 url = QUrl("qrc:/test2.html"); 1300 frame->setUrl(url); 1301 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 1302 expectedLoadFinishedCount++; 1303 QCOMPARE(spy.count(), expectedLoadFinishedCount); 1304 QCOMPARE(frame->url(), url); 1305 QCOMPARE(frame->requestedUrl(), url); 1306 QCOMPARE(m_page->history()->count(), 2); 1307} 1308 1309void tst_QWebFrame::setUrlUsingStateObject() 1310{ 1311 const QUrl aboutBlank("about:blank"); 1312 QUrl url; 1313 QWebFrame* frame = m_page->mainFrame(); 1314 QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl))); 1315 int expectedUrlChangeCount = 0; 1316 1317 QCOMPARE(m_page->history()->count(), 0); 1318 1319 url = QUrl("qrc:/test1.html"); 1320 frame->setUrl(url); 1321 waitForSignal(m_page, SIGNAL(loadFinished(bool))); 1322 expectedUrlChangeCount++; 1323 QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); 1324 QCOMPARE(frame->url(), url); 1325 QCOMPARE(m_page->history()->count(), 1); 1326 1327 frame->evaluateJavaScript("window.history.pushState(null,'push', 'navigate/to/here')"); 1328 expectedUrlChangeCount++; 1329 QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); 1330 QCOMPARE(frame->url(), QUrl("qrc:/navigate/to/here")); 1331 QCOMPARE(m_page->history()->count(), 2); 1332 QVERIFY(m_page->history()->canGoBack()); 1333 1334 frame->evaluateJavaScript("window.history.replaceState(null,'replace', 'another/location')"); 1335 expectedUrlChangeCount++; 1336 QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); 1337 QCOMPARE(frame->url(), QUrl("qrc:/navigate/to/another/location")); 1338 QCOMPARE(m_page->history()->count(), 2); 1339 QVERIFY(!m_page->history()->canGoForward()); 1340 QVERIFY(m_page->history()->canGoBack()); 1341 1342 frame->evaluateJavaScript("window.history.back()"); 1343 QTest::qWait(100); 1344 expectedUrlChangeCount++; 1345 QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount); 1346 QCOMPARE(frame->url(), QUrl("qrc:/test1.html")); 1347 QVERIFY(m_page->history()->canGoForward()); 1348 QVERIFY(!m_page->history()->canGoBack()); 1349} 1350 1351void tst_QWebFrame::setUrlSameUrl() 1352{ 1353 const QUrl url1("qrc:/test1.html"); 1354 const QUrl url2("qrc:/test2.html"); 1355 1356 QWebPage page; 1357 QWebFrame* frame = page.mainFrame(); 1358 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 1359 page.setNetworkAccessManager(networkManager); 1360 1361 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 1362 1363 frame->setUrl(url1); 1364 waitForSignal(frame, SIGNAL(loadFinished(bool))); 1365 QVERIFY(frame->url() != url1); // Nota bene: our QNAM redirects url1 to url2 1366 QCOMPARE(frame->url(), url2); 1367 QCOMPARE(spy.count(), 1); 1368 1369 frame->setUrl(url1); 1370 waitForSignal(frame, SIGNAL(loadFinished(bool))); 1371 QVERIFY(frame->url() != url1); 1372 QCOMPARE(frame->url(), url2); 1373 QCOMPARE(spy.count(), 2); 1374 1375 // Now a case without redirect. The existing behavior we have for setUrl() 1376 // is more like a "clear(); load()", so the page will be loaded again, even 1377 // if urlToBeLoaded == url(). This test should be changed if we want to 1378 // make setUrl() early return in this case. 1379 frame->setUrl(url2); 1380 waitForSignal(frame, SIGNAL(loadFinished(bool))); 1381 QCOMPARE(frame->url(), url2); 1382 QCOMPARE(spy.count(), 3); 1383 1384 frame->setUrl(url1); 1385 waitForSignal(frame, SIGNAL(loadFinished(bool))); 1386 QCOMPARE(frame->url(), url2); 1387 QCOMPARE(spy.count(), 4); 1388} 1389 1390static inline QUrl extractBaseUrl(const QUrl& url) 1391{ 1392 return url.resolved(QUrl()); 1393} 1394 1395void tst_QWebFrame::setUrlThenLoads_data() 1396{ 1397 QTest::addColumn<QUrl>("url"); 1398 QTest::addColumn<QUrl>("baseUrl"); 1399 1400 QTest::newRow("resource file") << QUrl("qrc:/test1.html") << extractBaseUrl(QUrl("qrc:/test1.html")); 1401 QTest::newRow("base specified in HTML") << QUrl("data:text/html,<head><base href=\"http://different.base/\"></head>") << QUrl("http://different.base/"); 1402} 1403 1404void tst_QWebFrame::setUrlThenLoads() 1405{ 1406 QFETCH(QUrl, url); 1407 QFETCH(QUrl, baseUrl); 1408 QWebFrame* frame = m_page->mainFrame(); 1409 QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl))); 1410 QSignalSpy startedSpy(frame, SIGNAL(loadStarted())); 1411 QSignalSpy finishedSpy(frame, SIGNAL(loadFinished(bool))); 1412 1413 frame->setUrl(url); 1414 QCOMPARE(startedSpy.count(), 1); 1415 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 1416 QCOMPARE(urlChangedSpy.count(), 1); 1417 QVERIFY(finishedSpy.at(0).first().toBool()); 1418 QCOMPARE(frame->url(), url); 1419 QCOMPARE(frame->requestedUrl(), url); 1420 QCOMPARE(frame->baseUrl(), baseUrl); 1421 1422 const QUrl urlToLoad1("qrc:/test2.html"); 1423 const QUrl urlToLoad2("qrc:/test1.html"); 1424 1425 // Just after first load. URL didn't changed yet. 1426 frame->load(urlToLoad1); 1427 QCOMPARE(startedSpy.count(), 2); 1428 QCOMPARE(frame->url(), url); 1429 QCOMPARE(frame->requestedUrl(), urlToLoad1); 1430 QCOMPARE(frame->baseUrl(), baseUrl); 1431 1432 // After first URL changed. 1433 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 1434 QCOMPARE(urlChangedSpy.count(), 2); 1435 QVERIFY(finishedSpy.at(1).first().toBool()); 1436 QCOMPARE(frame->url(), urlToLoad1); 1437 QCOMPARE(frame->requestedUrl(), urlToLoad1); 1438 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); 1439 1440 // Just after second load. URL didn't changed yet. 1441 frame->load(urlToLoad2); 1442 QCOMPARE(startedSpy.count(), 3); 1443 QCOMPARE(frame->url(), urlToLoad1); 1444 QCOMPARE(frame->requestedUrl(), urlToLoad2); 1445 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); 1446 1447 // After second URL changed. 1448 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 1449 QCOMPARE(urlChangedSpy.count(), 3); 1450 QVERIFY(finishedSpy.at(2).first().toBool()); 1451 QCOMPARE(frame->url(), urlToLoad2); 1452 QCOMPARE(frame->requestedUrl(), urlToLoad2); 1453 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad2)); 1454} 1455 1456void tst_QWebFrame::loadFinishedAfterNotFoundError() 1457{ 1458 QWebPage page; 1459 QWebFrame* frame = page.mainFrame(); 1460 1461 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 1462 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 1463 page.setNetworkAccessManager(networkManager); 1464 1465 frame->setUrl(FakeReply::urlFor404ErrorWithoutContents); 1466 QTRY_COMPARE(spy.count(), 1); 1467 const bool wasLoadOk = spy.at(0).at(0).toBool(); 1468 QVERIFY(!wasLoadOk); 1469} 1470 1471class URLSetter : public QObject { 1472 Q_OBJECT 1473 1474public: 1475 enum Signal { 1476 LoadStarted, 1477 LoadFinished, 1478 ProvisionalLoad 1479 }; 1480 1481 enum Type { 1482 UseLoad, 1483 UseSetUrl 1484 }; 1485 1486 URLSetter(QWebFrame*, Signal, Type, const QUrl&); 1487 1488public Q_SLOTS: 1489 void execute(); 1490 1491Q_SIGNALS: 1492 void finished(); 1493 1494private: 1495 QWebFrame* m_frame; 1496 QUrl m_url; 1497 Type m_type; 1498}; 1499 1500Q_DECLARE_METATYPE(URLSetter::Signal) 1501Q_DECLARE_METATYPE(URLSetter::Type) 1502 1503URLSetter::URLSetter(QWebFrame* frame, Signal signal, URLSetter::Type type, const QUrl& url) 1504 : m_frame(frame), m_url(url), m_type(type) 1505{ 1506 if (signal == LoadStarted) 1507 connect(m_frame, SIGNAL(loadStarted()), SLOT(execute())); 1508 else if (signal == LoadFinished) 1509 connect(m_frame, SIGNAL(loadFinished(bool)), SLOT(execute())); 1510 else 1511 connect(m_frame, SIGNAL(provisionalLoad()), SLOT(execute())); 1512} 1513 1514void URLSetter::execute() 1515{ 1516 // We track only the first emission. 1517 m_frame->disconnect(this); 1518 if (m_type == URLSetter::UseLoad) 1519 m_frame->load(m_url); 1520 else 1521 m_frame->setUrl(m_url); 1522 connect(m_frame, SIGNAL(loadFinished(bool)), SIGNAL(finished())); 1523} 1524 1525void tst_QWebFrame::loadInSignalHandlers_data() 1526{ 1527 QTest::addColumn<URLSetter::Type>("type"); 1528 QTest::addColumn<URLSetter::Signal>("signal"); 1529 QTest::addColumn<QUrl>("url"); 1530 1531 const QUrl validUrl("qrc:/test2.html"); 1532 const QUrl invalidUrl("qrc:/invalid"); 1533 1534 QTest::newRow("call load() in loadStarted() after valid url") << URLSetter::UseLoad << URLSetter::LoadStarted << validUrl; 1535 QTest::newRow("call load() in loadStarted() after invalid url") << URLSetter::UseLoad << URLSetter::LoadStarted << invalidUrl; 1536 QTest::newRow("call load() in loadFinished() after valid url") << URLSetter::UseLoad << URLSetter::LoadFinished << validUrl; 1537 QTest::newRow("call load() in loadFinished() after invalid url") << URLSetter::UseLoad << URLSetter::LoadFinished << invalidUrl; 1538 QTest::newRow("call load() in provisionalLoad() after valid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << validUrl; 1539 QTest::newRow("call load() in provisionalLoad() after invalid url") << URLSetter::UseLoad << URLSetter::ProvisionalLoad << invalidUrl; 1540 1541 QTest::newRow("call setUrl() in loadStarted() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << validUrl; 1542 QTest::newRow("call setUrl() in loadStarted() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadStarted << invalidUrl; 1543 QTest::newRow("call setUrl() in loadFinished() after valid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << validUrl; 1544 QTest::newRow("call setUrl() in loadFinished() after invalid url") << URLSetter::UseSetUrl << URLSetter::LoadFinished << invalidUrl; 1545 QTest::newRow("call setUrl() in provisionalLoad() after valid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << validUrl; 1546 QTest::newRow("call setUrl() in provisionalLoad() after invalid url") << URLSetter::UseSetUrl << URLSetter::ProvisionalLoad << invalidUrl; 1547} 1548 1549void tst_QWebFrame::loadInSignalHandlers() 1550{ 1551 QFETCH(URLSetter::Type, type); 1552 QFETCH(URLSetter::Signal, signal); 1553 QFETCH(QUrl, url); 1554 1555 QWebFrame* frame = m_page->mainFrame(); 1556 const QUrl urlForSetter("qrc:/test1.html"); 1557 URLSetter setter(frame, signal, type, urlForSetter); 1558 1559 frame->load(url); 1560 waitForSignal(&setter, SIGNAL(finished()), 200); 1561 QCOMPARE(frame->url(), urlForSetter); 1562} 1563 1564QTEST_MAIN(tst_QWebFrame) 1565#include "tst_qwebframe.moc" 1566