tst_qwebpage.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
1/* 2 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 3 Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> 4 Copyright (C) 2010 Holger Hans Peter Freyther 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Library General Public 8 License as published by the Free Software Foundation; either 9 version 2 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Library General Public License for more details. 15 16 You should have received a copy of the GNU Library General Public License 17 along with this library; see the file COPYING.LIB. If not, write to 18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20*/ 21 22#include "../util.h" 23#include "../WebCoreSupport/DumpRenderTreeSupportQt.h" 24#include <QClipboard> 25#include <QDir> 26#include <QGraphicsWidget> 27#include <QLineEdit> 28#include <QMainWindow> 29#include <QMenu> 30#include <QPushButton> 31#include <QStyle> 32#include <QtTest/QtTest> 33#include <QTextCharFormat> 34#include <qgraphicsscene.h> 35#include <qgraphicsview.h> 36#include <qgraphicswebview.h> 37#include <qnetworkcookiejar.h> 38#include <qnetworkrequest.h> 39#include <qwebdatabase.h> 40#include <qwebelement.h> 41#include <qwebframe.h> 42#include <qwebhistory.h> 43#include <qwebpage.h> 44#include <qwebsecurityorigin.h> 45#include <qwebview.h> 46#include <qimagewriter.h> 47 48class EventSpy : public QObject, public QList<QEvent::Type> 49{ 50 Q_OBJECT 51public: 52 EventSpy(QObject* objectToSpy) 53 { 54 objectToSpy->installEventFilter(this); 55 } 56 57 virtual bool eventFilter(QObject* receiver, QEvent* event) 58 { 59 append(event->type()); 60 return false; 61 } 62}; 63 64class tst_QWebPage : public QObject 65{ 66 Q_OBJECT 67 68public: 69 tst_QWebPage(); 70 virtual ~tst_QWebPage(); 71 72public slots: 73 void init(); 74 void cleanup(); 75 void cleanupFiles(); 76 77private slots: 78 void initTestCase(); 79 void cleanupTestCase(); 80 81 void contextMenuCopy(); 82 void acceptNavigationRequest(); 83 void geolocationRequestJS(); 84 void loadFinished(); 85 void acceptNavigationRequestWithNewWindow(); 86 void userStyleSheet(); 87 void loadHtml5Video(); 88 void modified(); 89 void contextMenuCrash(); 90 void updatePositionDependentActionsCrash(); 91 void database(); 92 void createPluginWithPluginsEnabled(); 93 void createPluginWithPluginsDisabled(); 94 void destroyPlugin_data(); 95 void destroyPlugin(); 96 void createViewlessPlugin_data(); 97 void createViewlessPlugin(); 98 void graphicsWidgetPlugin(); 99 void multiplePageGroupsAndLocalStorage(); 100 void cursorMovements(); 101 void textSelection(); 102 void textEditing(); 103 void backActionUpdate(); 104 void frameAt(); 105 void requestCache(); 106 void loadCachedPage(); 107 void protectBindingsRuntimeObjectsFromCollector(); 108 void localURLSchemes(); 109 void testOptionalJSObjects(); 110 void testEnablePersistentStorage(); 111 void consoleOutput(); 112 void inputMethods_data(); 113 void inputMethods(); 114 void inputMethodsTextFormat_data(); 115 void inputMethodsTextFormat(); 116 void defaultTextEncoding(); 117 void errorPageExtension(); 118 void errorPageExtensionInIFrames(); 119 void errorPageExtensionInFrameset(); 120 void userAgentApplicationName(); 121 122 void viewModes(); 123 124 void crashTests_LazyInitializationOfMainFrame(); 125 126 void screenshot_data(); 127 void screenshot(); 128 129 void originatingObjectInNetworkRequests(); 130 void testJSPrompt(); 131 void showModalDialog(); 132 void testStopScheduledPageRefresh(); 133 void findText(); 134 void supportedContentType(); 135 void infiniteLoopJS(); 136 void navigatorCookieEnabled(); 137 void deleteQWebViewTwice(); 138 139#ifdef Q_OS_MAC 140 void macCopyUnicodeToClipboard(); 141#endif 142 143private: 144 QWebView* m_view; 145 QWebPage* m_page; 146}; 147 148tst_QWebPage::tst_QWebPage() 149{ 150} 151 152tst_QWebPage::~tst_QWebPage() 153{ 154} 155 156void tst_QWebPage::init() 157{ 158 m_view = new QWebView(); 159 m_page = m_view->page(); 160} 161 162void tst_QWebPage::cleanup() 163{ 164 delete m_view; 165} 166 167void tst_QWebPage::cleanupFiles() 168{ 169 QFile::remove("Databases.db"); 170 QDir::current().rmdir("http_www.myexample.com_0"); 171 QFile::remove("http_www.myexample.com_0.localstorage"); 172} 173 174void tst_QWebPage::initTestCase() 175{ 176 cleanupFiles(); // In case there are old files from previous runs 177} 178 179void tst_QWebPage::cleanupTestCase() 180{ 181 cleanupFiles(); // Be nice 182} 183 184class NavigationRequestOverride : public QWebPage 185{ 186public: 187 NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {} 188 189 bool m_acceptNavigationRequest; 190protected: 191 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) { 192 Q_UNUSED(frame); 193 Q_UNUSED(request); 194 Q_UNUSED(type); 195 196 return m_acceptNavigationRequest; 197 } 198}; 199 200void tst_QWebPage::acceptNavigationRequest() 201{ 202 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); 203 204 NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false); 205 m_view->setPage(newPage); 206 207 m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>" 208 "<input type='text'><input type='submit'></form></body></html>"), QUrl()); 209 QTRY_COMPARE(loadSpy.count(), 1); 210 211 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); 212 213 newPage->m_acceptNavigationRequest = true; 214 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); 215 QTRY_COMPARE(loadSpy.count(), 2); 216 217 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?")); 218 219 // Restore default page 220 m_view->setPage(0); 221} 222 223class JSTestPage : public QWebPage 224{ 225Q_OBJECT 226public: 227 JSTestPage(QObject* parent = 0) 228 : QWebPage(parent) {} 229 230public slots: 231 bool shouldInterruptJavaScript() { 232 return true; 233 } 234 void requestPermission(QWebFrame* frame, QWebPage::Feature feature) 235 { 236 if (m_allowGeolocation) 237 setFeaturePermission(frame, feature, PermissionGrantedByUser); 238 else 239 setFeaturePermission(frame, feature, PermissionDeniedByUser); 240 } 241 242public: 243 void setGeolocationPermission(bool allow) 244 { 245 m_allowGeolocation = allow; 246 } 247 248private: 249 bool m_allowGeolocation; 250}; 251 252void tst_QWebPage::infiniteLoopJS() 253{ 254 JSTestPage* newPage = new JSTestPage(m_view); 255 m_view->setPage(newPage); 256 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); 257 m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}"); 258 delete newPage; 259} 260 261void tst_QWebPage::geolocationRequestJS() 262{ 263 JSTestPage* newPage = new JSTestPage(m_view); 264 265 if (newPage->mainFrame()->evaluateJavaScript(QLatin1String("!navigator.geolocation")).toBool()) { 266 delete newPage; 267 QSKIP("Geolocation is not supported.", SkipSingle); 268 } 269 270 connect(newPage, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)), 271 newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature))); 272 273 newPage->setGeolocationPermission(false); 274 m_view->setPage(newPage); 275 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); 276 m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); 277 QTest::qWait(2000); 278 QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode"); 279 280 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0); 281 282 newPage->setGeolocationPermission(true); 283 m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);"); 284 empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode"); 285 286 //http://dev.w3.org/geo/api/spec-source.html#position 287 //PositionError: const unsigned short PERMISSION_DENIED = 1; 288 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1); 289 delete newPage; 290} 291 292void tst_QWebPage::loadFinished() 293{ 294 qRegisterMetaType<QWebFrame*>("QWebFrame*"); 295 qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*"); 296 QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted())); 297 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 298 299 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 300 "<head><meta http-equiv='refresh' content='1'></head>foo \">" 301 "<frame src=\"data:text/html,bar\"></frameset>")); 302 QTRY_COMPARE(spyLoadFinished.count(), 1); 303 304 QTRY_VERIFY(spyLoadStarted.count() > 1); 305 QTRY_VERIFY(spyLoadFinished.count() > 1); 306 307 spyLoadFinished.clear(); 308 309 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 310 "foo \"><frame src=\"data:text/html,bar\"></frameset>")); 311 QTRY_COMPARE(spyLoadFinished.count(), 1); 312 QCOMPARE(spyLoadFinished.count(), 1); 313} 314 315class ConsolePage : public QWebPage 316{ 317public: 318 ConsolePage(QObject* parent = 0) : QWebPage(parent) {} 319 320 virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) 321 { 322 messages.append(message); 323 lineNumbers.append(lineNumber); 324 sourceIDs.append(sourceID); 325 } 326 327 QStringList messages; 328 QList<int> lineNumbers; 329 QStringList sourceIDs; 330}; 331 332void tst_QWebPage::consoleOutput() 333{ 334 ConsolePage page; 335 page.mainFrame()->evaluateJavaScript("this is not valid JavaScript"); 336 QCOMPARE(page.messages.count(), 1); 337 QCOMPARE(page.lineNumbers.at(0), 1); 338} 339 340class TestPage : public QWebPage 341{ 342public: 343 TestPage(QObject* parent = 0) : QWebPage(parent) {} 344 345 struct Navigation { 346 QPointer<QWebFrame> frame; 347 QNetworkRequest request; 348 NavigationType type; 349 }; 350 351 QList<Navigation> navigations; 352 QList<QWebPage*> createdWindows; 353 354 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) { 355 Navigation n; 356 n.frame = frame; 357 n.request = request; 358 n.type = type; 359 navigations.append(n); 360 return true; 361 } 362 363 virtual QWebPage* createWindow(WebWindowType) { 364 QWebPage* page = new TestPage(this); 365 createdWindows.append(page); 366 return page; 367 } 368}; 369 370void tst_QWebPage::acceptNavigationRequestWithNewWindow() 371{ 372 TestPage* page = new TestPage(m_view); 373 page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true); 374 m_page = page; 375 m_view->setPage(m_page); 376 377 m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); 378 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 379 380 QFocusEvent fe(QEvent::FocusIn); 381 m_page->event(&fe); 382 383 QVERIFY(m_page->focusNextPrevChild(/*next*/ true)); 384 385 QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); 386 m_page->event(&keyEnter); 387 388 QCOMPARE(page->navigations.count(), 2); 389 390 TestPage::Navigation n = page->navigations.at(1); 391 QVERIFY(n.frame.isNull()); 392 QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached")); 393 QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked); 394 395 QCOMPARE(page->createdWindows.count(), 1); 396} 397 398class TestNetworkManager : public QNetworkAccessManager 399{ 400public: 401 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} 402 403 QList<QUrl> requestedUrls; 404 QList<QNetworkRequest> requests; 405 406protected: 407 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { 408 requests.append(request); 409 requestedUrls.append(request.url()); 410 return QNetworkAccessManager::createRequest(op, request, outgoingData); 411 } 412}; 413 414void tst_QWebPage::userStyleSheet() 415{ 416 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 417 m_page->setNetworkAccessManager(networkManager); 418 networkManager->requestedUrls.clear(); 419 420 m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," 421 + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); 422 m_view->setHtml("<p>hello world</p>"); 423 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 424 425 QVERIFY(networkManager->requestedUrls.count() >= 1); 426 QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); 427} 428 429void tst_QWebPage::loadHtml5Video() 430{ 431#if defined(ENABLE_QT_MULTIMEDIA) && ENABLE_QT_MULTIMEDIA 432 QByteArray url("http://does.not/exist?a=1%2Cb=2"); 433 m_view->setHtml("<p><video id ='video' src='" + url + "' autoplay/></p>"); 434 QTest::qWait(2000); 435 QUrl mUrl = DumpRenderTreeSupportQt::mediaContentUrlByElementId(m_page->mainFrame(), "video"); 436 QCOMPARE(mUrl.toEncoded(), url); 437#else 438 QSKIP("This test requires Qt Multimedia", SkipAll); 439#endif 440} 441 442void tst_QWebPage::viewModes() 443{ 444 m_view->setHtml("<body></body>"); 445 m_page->setProperty("_q_viewMode", "minimized"); 446 447 QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")"); 448 QVERIFY(empty.type() == QVariant::Bool && empty.toBool()); 449 450 QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")"); 451 QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool()); 452 453 QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")"); 454 QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool()); 455} 456 457void tst_QWebPage::modified() 458{ 459 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub")); 460 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 461 462 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah")); 463 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 464 465 QVERIFY(!m_page->isModified()); 466 467// m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))"); 468 m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()"); 469 m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');"); 470 471 QVERIFY(m_page->isModified()); 472 473 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);"); 474 475 QVERIFY(!m_page->isModified()); 476 477 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);"); 478 479 QVERIFY(m_page->isModified()); 480 481 QVERIFY(m_page->history()->canGoBack()); 482 QVERIFY(!m_page->history()->canGoForward()); 483 QCOMPARE(m_page->history()->count(), 2); 484 QVERIFY(m_page->history()->backItem().isValid()); 485 QVERIFY(!m_page->history()->forwardItem().isValid()); 486 487 m_page->history()->back(); 488 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 489 490 QVERIFY(!m_page->history()->canGoBack()); 491 QVERIFY(m_page->history()->canGoForward()); 492 493 QVERIFY(!m_page->isModified()); 494 495 QVERIFY(m_page->history()->currentItemIndex() == 0); 496 497 m_page->history()->setMaximumItemCount(3); 498 QVERIFY(m_page->history()->maximumItemCount() == 3); 499 500 QVariant variant("string test"); 501 m_page->history()->currentItem().setUserData(variant); 502 QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); 503 504 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page")); 505 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page")); 506 QVERIFY(m_page->history()->count() == 2); 507 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page")); 508 QVERIFY(m_page->history()->count() == 2); 509 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page")); 510 QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*)))); 511} 512 513// https://bugs.webkit.org/show_bug.cgi?id=51331 514void tst_QWebPage::updatePositionDependentActionsCrash() 515{ 516 QWebView view; 517 view.setHtml("<p>test"); 518 QPoint pos(0, 0); 519 view.page()->updatePositionDependentActions(pos); 520 QMenu* contextMenu = 0; 521 foreach (QObject* child, view.children()) { 522 contextMenu = qobject_cast<QMenu*>(child); 523 if (contextMenu) 524 break; 525 } 526 QVERIFY(!contextMenu); 527} 528 529// https://bugs.webkit.org/show_bug.cgi?id=20357 530void tst_QWebPage::contextMenuCrash() 531{ 532 QWebView view; 533 view.setHtml("<p>test"); 534 QPoint pos(0, 0); 535 QContextMenuEvent event(QContextMenuEvent::Mouse, pos); 536 view.page()->swallowContextMenuEvent(&event); 537 view.page()->updatePositionDependentActions(pos); 538 QMenu* contextMenu = 0; 539 foreach (QObject* child, view.children()) { 540 contextMenu = qobject_cast<QMenu*>(child); 541 if (contextMenu) 542 break; 543 } 544 QVERIFY(contextMenu); 545 delete contextMenu; 546} 547 548void tst_QWebPage::database() 549{ 550 QString path = QDir::currentPath(); 551 m_page->settings()->setOfflineStoragePath(path); 552 QVERIFY(m_page->settings()->offlineStoragePath() == path); 553 554 QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024); 555 QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024); 556 557 m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 558 m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); 559 560 QString dbFileName = path + "Databases.db"; 561 562 if (QFile::exists(dbFileName)) 563 QFile::remove(dbFileName); 564 565 qRegisterMetaType<QWebFrame*>("QWebFrame*"); 566 QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString))); 567 m_view->setHtml(QString("<html><head><script>var db; db=openDatabase('testdb', '1.0', 'test database API', 50000); </script></head><body><div></div></body></html>"), QUrl("http://www.myexample.com")); 568 QTRY_COMPARE(spy.count(), 1); 569 m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);"); 570 QTRY_COMPARE(spy.count(),1); 571 572 m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';"); 573 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); 574 575 QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test"); 576 QCOMPARE(s1.toString(), QString("This is a test for local storage")); 577 578 m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';"); 579 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); 580 QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test"); 581 QCOMPARE(s2.toString(), QString("This is a test for session storage")); 582 583 m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com")); 584 m_page->mainFrame()->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });"); 585 QTest::qWait(200); 586 587 // Remove all databases. 588 QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin(); 589 QList<QWebDatabase> dbs = origin.databases(); 590 for (int i = 0; i < dbs.count(); i++) { 591 QString fileName = dbs[i].fileName(); 592 QVERIFY(QFile::exists(fileName)); 593 QWebDatabase::removeDatabase(dbs[i]); 594 QVERIFY(!QFile::exists(fileName)); 595 } 596 QVERIFY(!origin.databases().size()); 597 // Remove removed test :-) 598 QWebDatabase::removeAllDatabases(); 599 QVERIFY(!origin.databases().size()); 600} 601 602class PluginPage : public QWebPage 603{ 604public: 605 PluginPage(QObject *parent = 0) 606 : QWebPage(parent) {} 607 608 struct CallInfo 609 { 610 CallInfo(const QString &c, const QUrl &u, 611 const QStringList &pn, const QStringList &pv, 612 QObject *r) 613 : classid(c), url(u), paramNames(pn), 614 paramValues(pv), returnValue(r) 615 {} 616 QString classid; 617 QUrl url; 618 QStringList paramNames; 619 QStringList paramValues; 620 QObject *returnValue; 621 }; 622 623 QList<CallInfo> calls; 624 625protected: 626 virtual QObject *createPlugin(const QString &classid, const QUrl &url, 627 const QStringList ¶mNames, 628 const QStringList ¶mValues) 629 { 630 QObject *result = 0; 631 if (classid == "pushbutton") 632 result = new QPushButton(); 633#ifndef QT_NO_INPUTDIALOG 634 else if (classid == "lineedit") 635 result = new QLineEdit(); 636#endif 637 else if (classid == "graphicswidget") 638 result = new QGraphicsWidget(); 639 if (result) 640 result->setObjectName(classid); 641 calls.append(CallInfo(classid, url, paramNames, paramValues, result)); 642 return result; 643 } 644}; 645 646static void createPlugin(QWebView *view) 647{ 648 QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool))); 649 650 PluginPage* newPage = new PluginPage(view); 651 view->setPage(newPage); 652 653 // type has to be application/x-qt-plugin 654 view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>")); 655 QTRY_COMPARE(loadSpy.count(), 1); 656 QCOMPARE(newPage->calls.count(), 0); 657 658 view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>")); 659 QTRY_COMPARE(loadSpy.count(), 2); 660 QCOMPARE(newPage->calls.count(), 1); 661 { 662 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 663 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); 664 QCOMPARE(ci.url, QUrl()); 665 QCOMPARE(ci.paramNames.count(), 3); 666 QCOMPARE(ci.paramValues.count(), 3); 667 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 668 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 669 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 670 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); 671 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 672 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); 673 QVERIFY(ci.returnValue != 0); 674 QVERIFY(ci.returnValue->inherits("QPushButton")); 675 } 676 // test JS bindings 677 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(), 678 QString::fromLatin1("[object HTMLObjectElement]")); 679 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(), 680 QString::fromLatin1("[object HTMLObjectElement]")); 681 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(), 682 QString::fromLatin1("string")); 683 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(), 684 QString::fromLatin1("pushbutton")); 685 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(), 686 QString::fromLatin1("function")); 687 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(), 688 QString::fromLatin1("function clicked() {\n [native code]\n}")); 689 690 view->setHtml(QString("<html><body><table>" 691 "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>" 692 "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>" 693 "</table></body></html>"), QUrl("http://foo.bar.baz")); 694 QTRY_COMPARE(loadSpy.count(), 3); 695 QCOMPARE(newPage->calls.count(), 2); 696 { 697 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 698 QCOMPARE(ci.classid, QString::fromLatin1("lineedit")); 699 QCOMPARE(ci.url, QUrl()); 700 QCOMPARE(ci.paramNames.count(), 3); 701 QCOMPARE(ci.paramValues.count(), 3); 702 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 703 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 704 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 705 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit")); 706 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 707 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit")); 708 QVERIFY(ci.returnValue != 0); 709 QVERIFY(ci.returnValue->inherits("QLineEdit")); 710 } 711 { 712 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 713 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); 714 QCOMPARE(ci.url, QUrl()); 715 QCOMPARE(ci.paramNames.count(), 3); 716 QCOMPARE(ci.paramValues.count(), 3); 717 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 718 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 719 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 720 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); 721 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 722 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); 723 QVERIFY(ci.returnValue != 0); 724 QVERIFY(ci.returnValue->inherits("QPushButton")); 725 } 726} 727 728void tst_QWebPage::graphicsWidgetPlugin() 729{ 730 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 731 QGraphicsWebView webView; 732 733 QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); 734 735 PluginPage* newPage = new PluginPage(&webView); 736 webView.setPage(newPage); 737 738 // type has to be application/x-qt-plugin 739 webView.setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>")); 740 QTRY_COMPARE(loadSpy.count(), 1); 741 QCOMPARE(newPage->calls.count(), 0); 742 743 webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>")); 744 QTRY_COMPARE(loadSpy.count(), 2); 745 QCOMPARE(newPage->calls.count(), 1); 746 { 747 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 748 QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget")); 749 QCOMPARE(ci.url, QUrl()); 750 QCOMPARE(ci.paramNames.count(), 3); 751 QCOMPARE(ci.paramValues.count(), 3); 752 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 753 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 754 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 755 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget")); 756 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 757 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget")); 758 QVERIFY(ci.returnValue); 759 QVERIFY(ci.returnValue->inherits("QGraphicsWidget")); 760 } 761 // test JS bindings 762 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(), 763 QString::fromLatin1("[object HTMLObjectElement]")); 764 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.toString()").toString(), 765 QString::fromLatin1("[object HTMLObjectElement]")); 766 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(), 767 QString::fromLatin1("string")); 768 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.objectName").toString(), 769 QString::fromLatin1("graphicswidget")); 770 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(), 771 QString::fromLatin1("function")); 772 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(), 773 QString::fromLatin1("function geometryChanged() {\n [native code]\n}")); 774} 775 776void tst_QWebPage::createPluginWithPluginsEnabled() 777{ 778 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 779 createPlugin(m_view); 780} 781 782void tst_QWebPage::createPluginWithPluginsDisabled() 783{ 784 // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is 785 // false. The client decides whether a Qt plugin is enabled or not when 786 // it decides whether or not to instantiate it. 787 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false); 788 createPlugin(m_view); 789} 790 791// Standard base class for template PluginTracerPage. In tests it is used as interface. 792class PluginCounterPage : public QWebPage { 793public: 794 int m_count; 795 QPointer<QObject> m_widget; 796 QObject* m_pluginParent; 797 PluginCounterPage(QObject* parent = 0) 798 : QWebPage(parent) 799 , m_count(0) 800 , m_widget(0) 801 , m_pluginParent(0) 802 { 803 settings()->setAttribute(QWebSettings::PluginsEnabled, true); 804 } 805 ~PluginCounterPage() 806 { 807 if (m_pluginParent) 808 m_pluginParent->deleteLater(); 809 } 810}; 811 812template<class T> 813class PluginTracerPage : public PluginCounterPage { 814public: 815 PluginTracerPage(QObject* parent = 0) 816 : PluginCounterPage(parent) 817 { 818 // this is a dummy parent object for the created plugin 819 m_pluginParent = new T; 820 } 821 virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&) 822 { 823 m_count++; 824 m_widget = new T; 825 // need a cast to the specific type, as QObject::setParent cannot be called, 826 // because it is not virtual. Instead it is necesary to call QWidget::setParent, 827 // which also takes a QWidget* instead of a QObject*. Therefore we need to 828 // upcast to T*, which is a QWidget. 829 static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent)); 830 return m_widget; 831 } 832}; 833 834class PluginFactory { 835public: 836 enum FactoredType {QWidgetType, QGraphicsWidgetType}; 837 static PluginCounterPage* create(FactoredType type, QObject* parent = 0) 838 { 839 PluginCounterPage* result = 0; 840 switch (type) { 841 case QWidgetType: 842 result = new PluginTracerPage<QWidget>(parent); 843 break; 844 case QGraphicsWidgetType: 845 result = new PluginTracerPage<QGraphicsWidget>(parent); 846 break; 847 default: {/*Oops*/}; 848 } 849 return result; 850 } 851 852 static void prepareTestData() 853 { 854 QTest::addColumn<int>("type"); 855 QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType; 856 QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType; 857 } 858}; 859 860void tst_QWebPage::destroyPlugin_data() 861{ 862 PluginFactory::prepareTestData(); 863} 864 865void tst_QWebPage::destroyPlugin() 866{ 867 QFETCH(int, type); 868 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view); 869 m_view->setPage(page); 870 871 // we create the plugin, so the widget should be constructed 872 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); 873 m_view->setHtml(content); 874 QVERIFY(page->m_widget); 875 QCOMPARE(page->m_count, 1); 876 877 // navigate away, the plugin widget should be destructed 878 m_view->setHtml("<html><body>Hi</body></html>"); 879 QTestEventLoop::instance().enterLoop(1); 880 QVERIFY(!page->m_widget); 881} 882 883void tst_QWebPage::createViewlessPlugin_data() 884{ 885 PluginFactory::prepareTestData(); 886} 887 888void tst_QWebPage::createViewlessPlugin() 889{ 890 QFETCH(int, type); 891 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type); 892 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); 893 page->mainFrame()->setHtml(content); 894 QCOMPARE(page->m_count, 1); 895 QVERIFY(page->m_widget); 896 QVERIFY(page->m_pluginParent); 897 QVERIFY(page->m_widget->parent() == page->m_pluginParent); 898 delete page; 899 900} 901 902void tst_QWebPage::multiplePageGroupsAndLocalStorage() 903{ 904 QDir dir(QDir::currentPath()); 905 dir.mkdir("path1"); 906 dir.mkdir("path2"); 907 908 QWebView view1; 909 QWebView view2; 910 911 view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 912 view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1")); 913 DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1"); 914 view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 915 view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2")); 916 DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2"); 917 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1")); 918 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2")); 919 920 921 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 922 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 923 924 view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';"); 925 view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';"); 926 927 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 928 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 929 930 QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test"); 931 QCOMPARE(s1.toString(), QString("value1")); 932 933 QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test"); 934 QCOMPARE(s2.toString(), QString("value2")); 935 936 QTest::qWait(1000); 937 938 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage")); 939 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage")); 940 dir.rmdir(QDir::toNativeSeparators("./path1")); 941 dir.rmdir(QDir::toNativeSeparators("./path2")); 942} 943 944class CursorTrackedPage : public QWebPage 945{ 946public: 947 948 CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) { 949 setViewportSize(QSize(1024, 768)); // big space 950 } 951 952 QString selectedText() { 953 return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString(); 954 } 955 956 int selectionStartOffset() { 957 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt(); 958 } 959 960 int selectionEndOffset() { 961 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt(); 962 } 963 964 // true if start offset == end offset, i.e. no selected text 965 int isSelectionCollapsed() { 966 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool(); 967 } 968}; 969 970void tst_QWebPage::cursorMovements() 971{ 972 CursorTrackedPage* page = new CursorTrackedPage; 973 QString content("<html><body><p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>"); 974 page->mainFrame()->setHtml(content); 975 976 // this will select the first paragraph 977 QString script = "var range = document.createRange(); " \ 978 "var node = document.getElementById(\"one\"); " \ 979 "range.selectNode(node); " \ 980 "getSelection().addRange(range);"; 981 page->mainFrame()->evaluateJavaScript(script); 982 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); 983 QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>")); 984 985 // these actions must exist 986 QVERIFY(page->action(QWebPage::MoveToNextChar) != 0); 987 QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0); 988 QVERIFY(page->action(QWebPage::MoveToNextWord) != 0); 989 QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0); 990 QVERIFY(page->action(QWebPage::MoveToNextLine) != 0); 991 QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0); 992 QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0); 993 QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0); 994 QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0); 995 QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0); 996 QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0); 997 QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0); 998 999 // right now they are disabled because contentEditable is false 1000 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false); 1001 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false); 1002 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false); 1003 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false); 1004 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false); 1005 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false); 1006 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false); 1007 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false); 1008 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false); 1009 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false); 1010 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false); 1011 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false); 1012 1013 // make it editable before navigating the cursor 1014 page->setContentEditable(true); 1015 1016 // here the actions are enabled after contentEditable is true 1017 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true); 1018 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true); 1019 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true); 1020 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true); 1021 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true); 1022 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true); 1023 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true); 1024 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true); 1025 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true); 1026 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true); 1027 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true); 1028 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true); 1029 1030 // cursor will be before the word "jump" 1031 page->triggerAction(QWebPage::MoveToNextChar); 1032 QVERIFY(page->isSelectionCollapsed()); 1033 QCOMPARE(page->selectionStartOffset(), 0); 1034 1035 // cursor will be between 'j' and 'u' in the word "jump" 1036 page->triggerAction(QWebPage::MoveToNextChar); 1037 QVERIFY(page->isSelectionCollapsed()); 1038 QCOMPARE(page->selectionStartOffset(), 1); 1039 1040 // cursor will be between 'u' and 'm' in the word "jump" 1041 page->triggerAction(QWebPage::MoveToNextChar); 1042 QVERIFY(page->isSelectionCollapsed()); 1043 QCOMPARE(page->selectionStartOffset(), 2); 1044 1045 // cursor will be after the word "jump" 1046 page->triggerAction(QWebPage::MoveToNextWord); 1047 QVERIFY(page->isSelectionCollapsed()); 1048 QCOMPARE(page->selectionStartOffset(), 5); 1049 1050 // cursor will be after the word "lazy" 1051 page->triggerAction(QWebPage::MoveToNextWord); 1052 page->triggerAction(QWebPage::MoveToNextWord); 1053 page->triggerAction(QWebPage::MoveToNextWord); 1054 QVERIFY(page->isSelectionCollapsed()); 1055 QCOMPARE(page->selectionStartOffset(), 19); 1056 1057 // cursor will be between 'z' and 'y' in "lazy" 1058 page->triggerAction(QWebPage::MoveToPreviousChar); 1059 QVERIFY(page->isSelectionCollapsed()); 1060 QCOMPARE(page->selectionStartOffset(), 18); 1061 1062 // cursor will be between 'a' and 'z' in "lazy" 1063 page->triggerAction(QWebPage::MoveToPreviousChar); 1064 QVERIFY(page->isSelectionCollapsed()); 1065 QCOMPARE(page->selectionStartOffset(), 17); 1066 1067 // cursor will be before the word "lazy" 1068 page->triggerAction(QWebPage::MoveToPreviousWord); 1069 QVERIFY(page->isSelectionCollapsed()); 1070 QCOMPARE(page->selectionStartOffset(), 15); 1071 1072 // cursor will be before the word "quick" 1073 page->triggerAction(QWebPage::MoveToPreviousWord); 1074 page->triggerAction(QWebPage::MoveToPreviousWord); 1075 page->triggerAction(QWebPage::MoveToPreviousWord); 1076 page->triggerAction(QWebPage::MoveToPreviousWord); 1077 page->triggerAction(QWebPage::MoveToPreviousWord); 1078 page->triggerAction(QWebPage::MoveToPreviousWord); 1079 QVERIFY(page->isSelectionCollapsed()); 1080 QCOMPARE(page->selectionStartOffset(), 4); 1081 1082 // cursor will be between 'p' and 's' in the word "jumps" 1083 page->triggerAction(QWebPage::MoveToNextWord); 1084 page->triggerAction(QWebPage::MoveToNextWord); 1085 page->triggerAction(QWebPage::MoveToNextWord); 1086 page->triggerAction(QWebPage::MoveToNextChar); 1087 page->triggerAction(QWebPage::MoveToNextChar); 1088 page->triggerAction(QWebPage::MoveToNextChar); 1089 page->triggerAction(QWebPage::MoveToNextChar); 1090 page->triggerAction(QWebPage::MoveToNextChar); 1091 QVERIFY(page->isSelectionCollapsed()); 1092 QCOMPARE(page->selectionStartOffset(), 4); 1093 1094 // cursor will be before the word "jumps" 1095 page->triggerAction(QWebPage::MoveToStartOfLine); 1096 QVERIFY(page->isSelectionCollapsed()); 1097 QCOMPARE(page->selectionStartOffset(), 0); 1098 1099 // cursor will be after the word "dog" 1100 page->triggerAction(QWebPage::MoveToEndOfLine); 1101 QVERIFY(page->isSelectionCollapsed()); 1102 QCOMPARE(page->selectionStartOffset(), 23); 1103 1104 // cursor will be between 'w' and 'n' in "brown" 1105 page->triggerAction(QWebPage::MoveToStartOfLine); 1106 page->triggerAction(QWebPage::MoveToPreviousWord); 1107 page->triggerAction(QWebPage::MoveToPreviousWord); 1108 page->triggerAction(QWebPage::MoveToNextChar); 1109 page->triggerAction(QWebPage::MoveToNextChar); 1110 page->triggerAction(QWebPage::MoveToNextChar); 1111 page->triggerAction(QWebPage::MoveToNextChar); 1112 QVERIFY(page->isSelectionCollapsed()); 1113 QCOMPARE(page->selectionStartOffset(), 14); 1114 1115 // cursor will be after the word "fox" 1116 page->triggerAction(QWebPage::MoveToEndOfLine); 1117 QVERIFY(page->isSelectionCollapsed()); 1118 QCOMPARE(page->selectionStartOffset(), 19); 1119 1120 // cursor will be before the word "The" 1121 page->triggerAction(QWebPage::MoveToStartOfDocument); 1122 QVERIFY(page->isSelectionCollapsed()); 1123 QCOMPARE(page->selectionStartOffset(), 0); 1124 1125 // cursor will be after the word "you!" 1126 page->triggerAction(QWebPage::MoveToEndOfDocument); 1127 QVERIFY(page->isSelectionCollapsed()); 1128 QCOMPARE(page->selectionStartOffset(), 12); 1129 1130 // cursor will be before the word "be" 1131 page->triggerAction(QWebPage::MoveToStartOfBlock); 1132 QVERIFY(page->isSelectionCollapsed()); 1133 QCOMPARE(page->selectionStartOffset(), 0); 1134 1135 // cursor will be after the word "you!" 1136 page->triggerAction(QWebPage::MoveToEndOfBlock); 1137 QVERIFY(page->isSelectionCollapsed()); 1138 QCOMPARE(page->selectionStartOffset(), 12); 1139 1140 // try to move before the document start 1141 page->triggerAction(QWebPage::MoveToStartOfDocument); 1142 page->triggerAction(QWebPage::MoveToPreviousChar); 1143 QVERIFY(page->isSelectionCollapsed()); 1144 QCOMPARE(page->selectionStartOffset(), 0); 1145 page->triggerAction(QWebPage::MoveToStartOfDocument); 1146 page->triggerAction(QWebPage::MoveToPreviousWord); 1147 QVERIFY(page->isSelectionCollapsed()); 1148 QCOMPARE(page->selectionStartOffset(), 0); 1149 1150 // try to move past the document end 1151 page->triggerAction(QWebPage::MoveToEndOfDocument); 1152 page->triggerAction(QWebPage::MoveToNextChar); 1153 QVERIFY(page->isSelectionCollapsed()); 1154 QCOMPARE(page->selectionStartOffset(), 12); 1155 page->triggerAction(QWebPage::MoveToEndOfDocument); 1156 page->triggerAction(QWebPage::MoveToNextWord); 1157 QVERIFY(page->isSelectionCollapsed()); 1158 QCOMPARE(page->selectionStartOffset(), 12); 1159 1160 delete page; 1161} 1162 1163void tst_QWebPage::textSelection() 1164{ 1165 CursorTrackedPage* page = new CursorTrackedPage; 1166 QString content("<html><body><p id=one>The quick brown fox</p>" \ 1167 "<p id=two>jumps over the lazy dog</p>" \ 1168 "<p>May the source<br/>be with you!</p></body></html>"); 1169 page->mainFrame()->setHtml(content); 1170 1171 // these actions must exist 1172 QVERIFY(page->action(QWebPage::SelectAll) != 0); 1173 QVERIFY(page->action(QWebPage::SelectNextChar) != 0); 1174 QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0); 1175 QVERIFY(page->action(QWebPage::SelectNextWord) != 0); 1176 QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0); 1177 QVERIFY(page->action(QWebPage::SelectNextLine) != 0); 1178 QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0); 1179 QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0); 1180 QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0); 1181 QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0); 1182 QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0); 1183 QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0); 1184 QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0); 1185 1186 // right now they are disabled because contentEditable is false and 1187 // there isn't an existing selection to modify 1188 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false); 1189 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false); 1190 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false); 1191 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false); 1192 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false); 1193 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false); 1194 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false); 1195 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false); 1196 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false); 1197 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false); 1198 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false); 1199 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false); 1200 1201 // ..but SelectAll is awalys enabled 1202 QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true); 1203 1204 // Verify hasSelection returns false since there is no selection yet... 1205 QCOMPARE(page->hasSelection(), false); 1206 1207 // this will select the first paragraph 1208 QString selectScript = "var range = document.createRange(); " \ 1209 "var node = document.getElementById(\"one\"); " \ 1210 "range.selectNode(node); " \ 1211 "getSelection().addRange(range);"; 1212 page->mainFrame()->evaluateJavaScript(selectScript); 1213 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); 1214 QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>")); 1215 1216 // Make sure hasSelection returns true, since there is selected text now... 1217 QCOMPARE(page->hasSelection(), true); 1218 1219 // here the actions are enabled after a selection has been created 1220 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); 1221 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); 1222 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); 1223 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); 1224 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); 1225 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); 1226 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); 1227 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); 1228 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); 1229 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); 1230 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); 1231 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); 1232 1233 // make it editable before navigating the cursor 1234 page->setContentEditable(true); 1235 1236 // cursor will be before the word "The", this makes sure there is a charet 1237 page->triggerAction(QWebPage::MoveToStartOfDocument); 1238 QVERIFY(page->isSelectionCollapsed()); 1239 QCOMPARE(page->selectionStartOffset(), 0); 1240 1241 // here the actions are enabled after contentEditable is true 1242 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); 1243 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); 1244 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); 1245 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); 1246 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); 1247 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); 1248 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); 1249 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); 1250 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); 1251 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); 1252 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); 1253 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); 1254 1255 delete page; 1256} 1257 1258void tst_QWebPage::textEditing() 1259{ 1260 CursorTrackedPage* page = new CursorTrackedPage; 1261 QString content("<html><body><p id=one>The quick brown fox</p>" \ 1262 "<p id=two>jumps over the lazy dog</p>" \ 1263 "<p>May the source<br/>be with you!</p></body></html>"); 1264 page->mainFrame()->setHtml(content); 1265 1266 // these actions must exist 1267 QVERIFY(page->action(QWebPage::Cut) != 0); 1268 QVERIFY(page->action(QWebPage::Copy) != 0); 1269 QVERIFY(page->action(QWebPage::Paste) != 0); 1270 QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0); 1271 QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0); 1272 QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0); 1273 QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0); 1274 QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0); 1275 QVERIFY(page->action(QWebPage::ToggleBold) != 0); 1276 QVERIFY(page->action(QWebPage::ToggleItalic) != 0); 1277 QVERIFY(page->action(QWebPage::ToggleUnderline) != 0); 1278 QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0); 1279 QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0); 1280 QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0); 1281 QVERIFY(page->action(QWebPage::RemoveFormat) != 0); 1282 QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0); 1283 QVERIFY(page->action(QWebPage::ToggleSubscript) != 0); 1284 QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0); 1285 QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0); 1286 QVERIFY(page->action(QWebPage::InsertOrderedList) != 0); 1287 QVERIFY(page->action(QWebPage::Indent) != 0); 1288 QVERIFY(page->action(QWebPage::Outdent) != 0); 1289 QVERIFY(page->action(QWebPage::AlignCenter) != 0); 1290 QVERIFY(page->action(QWebPage::AlignJustified) != 0); 1291 QVERIFY(page->action(QWebPage::AlignLeft) != 0); 1292 QVERIFY(page->action(QWebPage::AlignRight) != 0); 1293 1294 // right now they are disabled because contentEditable is false 1295 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); 1296 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false); 1297 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false); 1298 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false); 1299 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false); 1300 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false); 1301 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false); 1302 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false); 1303 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false); 1304 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false); 1305 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false); 1306 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false); 1307 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false); 1308 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); 1309 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false); 1310 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false); 1311 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false); 1312 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false); 1313 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false); 1314 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false); 1315 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false); 1316 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false); 1317 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false); 1318 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false); 1319 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false); 1320 1321 // Select everything 1322 page->triggerAction(QWebPage::SelectAll); 1323 1324 // make sure it is enabled since there is a selection 1325 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true); 1326 1327 // make it editable before navigating the cursor 1328 page->setContentEditable(true); 1329 1330 // clear the selection 1331 page->triggerAction(QWebPage::MoveToStartOfDocument); 1332 QVERIFY(page->isSelectionCollapsed()); 1333 QCOMPARE(page->selectionStartOffset(), 0); 1334 1335 // make sure it is disabled since there isn't a selection 1336 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false); 1337 1338 // here the actions are enabled after contentEditable is true 1339 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true); 1340 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true); 1341 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true); 1342 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true); 1343 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true); 1344 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true); 1345 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true); 1346 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true); 1347 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true); 1348 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true); 1349 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true); 1350 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true); 1351 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true); 1352 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true); 1353 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true); 1354 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true); 1355 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true); 1356 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true); 1357 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true); 1358 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true); 1359 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true); 1360 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true); 1361 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true); 1362 1363 // make sure these are disabled since there isn't a selection 1364 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); 1365 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); 1366 1367 // make sure everything is selected 1368 page->triggerAction(QWebPage::SelectAll); 1369 1370 // this is only true if there is an editable selection 1371 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true); 1372 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true); 1373 1374 delete page; 1375} 1376 1377void tst_QWebPage::requestCache() 1378{ 1379 TestPage page; 1380 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); 1381 1382 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); 1383 QTRY_COMPARE(loadSpy.count(), 1); 1384 QTRY_COMPARE(page.navigations.count(), 1); 1385 1386 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>")); 1387 QTRY_COMPARE(loadSpy.count(), 2); 1388 QTRY_COMPARE(page.navigations.count(), 2); 1389 1390 page.triggerAction(QWebPage::Stop); 1391 QVERIFY(page.history()->canGoBack()); 1392 page.triggerAction(QWebPage::Back); 1393 1394 QTRY_COMPARE(loadSpy.count(), 3); 1395 QTRY_COMPARE(page.navigations.count(), 3); 1396 QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1397 (int)QNetworkRequest::PreferNetwork); 1398 QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1399 (int)QNetworkRequest::PreferNetwork); 1400 QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1401 (int)QNetworkRequest::PreferCache); 1402} 1403 1404void tst_QWebPage::loadCachedPage() 1405{ 1406 TestPage page; 1407 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); 1408 page.settings()->setMaximumPagesInCache(3); 1409 1410 page.mainFrame()->load(QUrl("data:text/html,This is first page")); 1411 1412 QTRY_COMPARE(loadSpy.count(), 1); 1413 QTRY_COMPARE(page.navigations.count(), 1); 1414 1415 QUrl firstPageUrl = page.mainFrame()->url(); 1416 page.mainFrame()->load(QUrl("data:text/html,This is second page")); 1417 1418 QTRY_COMPARE(loadSpy.count(), 2); 1419 QTRY_COMPARE(page.navigations.count(), 2); 1420 1421 page.triggerAction(QWebPage::Stop); 1422 QVERIFY(page.history()->canGoBack()); 1423 1424 QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl))); 1425 QVERIFY(urlSpy.isValid()); 1426 1427 page.triggerAction(QWebPage::Back); 1428 ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl))); 1429 QCOMPARE(urlSpy.size(), 1); 1430 1431 QList<QVariant> arguments1 = urlSpy.takeFirst(); 1432 QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl); 1433 1434} 1435void tst_QWebPage::backActionUpdate() 1436{ 1437 QWebView view; 1438 QWebPage *page = view.page(); 1439 QAction *action = page->action(QWebPage::Back); 1440 QVERIFY(!action->isEnabled()); 1441 QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool))); 1442 QUrl url = QUrl("qrc:///resources/framedindex.html"); 1443 page->mainFrame()->load(url); 1444 QTRY_COMPARE(loadSpy.count(), 1); 1445 QVERIFY(!action->isEnabled()); 1446 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10)); 1447 QTRY_COMPARE(loadSpy.count(), 2); 1448 1449 QVERIFY(action->isEnabled()); 1450} 1451 1452void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition) 1453{ 1454 if (!webFrame) 1455 return; 1456 1457 framePosition += QPoint(webFrame->pos()); 1458 QList<QWebFrame*> children = webFrame->childFrames(); 1459 for (int i = 0; i < children.size(); ++i) { 1460 if (children.at(i)->childFrames().size() > 0) 1461 frameAtHelper(webPage, children.at(i), framePosition); 1462 1463 QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size()); 1464 QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft())); 1465 } 1466} 1467 1468void tst_QWebPage::frameAt() 1469{ 1470 QWebView webView; 1471 QWebPage* webPage = webView.page(); 1472 QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); 1473 QUrl url = QUrl("qrc:///resources/iframe.html"); 1474 webPage->mainFrame()->load(url); 1475 QTRY_COMPARE(loadSpy.count(), 1); 1476 frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos()); 1477} 1478 1479void tst_QWebPage::inputMethods_data() 1480{ 1481 QTest::addColumn<QString>("viewType"); 1482 QTest::newRow("QWebView") << "QWebView"; 1483 QTest::newRow("QGraphicsWebView") << "QGraphicsWebView"; 1484} 1485 1486static Qt::InputMethodHints inputMethodHints(QObject* object) 1487{ 1488 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) 1489 return o->inputMethodHints(); 1490 if (QWidget* w = qobject_cast<QWidget*>(object)) 1491 return w->inputMethodHints(); 1492 return Qt::InputMethodHints(); 1493} 1494 1495static bool inputMethodEnabled(QObject* object) 1496{ 1497 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) 1498 return o->flags() & QGraphicsItem::ItemAcceptsInputMethod; 1499 if (QWidget* w = qobject_cast<QWidget*>(object)) 1500 return w->testAttribute(Qt::WA_InputMethodEnabled); 1501 return false; 1502} 1503 1504static void clickOnPage(QWebPage* page, const QPoint& position) 1505{ 1506 QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1507 page->event(&evpres); 1508 QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1509 page->event(&evrel); 1510} 1511 1512void tst_QWebPage::inputMethods() 1513{ 1514 QFETCH(QString, viewType); 1515 QWebPage* page = new QWebPage; 1516 QObject* view = 0; 1517 QObject* container = 0; 1518 if (viewType == "QWebView") { 1519 QWebView* wv = new QWebView; 1520 wv->setPage(page); 1521 view = wv; 1522 container = view; 1523 } else if (viewType == "QGraphicsWebView") { 1524 QGraphicsWebView* wv = new QGraphicsWebView; 1525 wv->setPage(page); 1526 view = wv; 1527 1528 QGraphicsView* gv = new QGraphicsView; 1529 QGraphicsScene* scene = new QGraphicsScene(gv); 1530 gv->setScene(scene); 1531 scene->addItem(wv); 1532 wv->setGeometry(QRect(0, 0, 500, 500)); 1533 1534 container = gv; 1535 } else 1536 QVERIFY2(false, "Unknown view type"); 1537 1538 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont"); 1539 page->mainFrame()->setHtml("<html><body>" \ 1540 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \ 1541 "<input type='password'/>" \ 1542 "</body></html>"); 1543 page->mainFrame()->setFocus(); 1544 1545 EventSpy viewEventSpy(container); 1546 1547 QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input"); 1548 QPoint textInputCenter = inputs.at(0).geometry().center(); 1549 1550 clickOnPage(page, textInputCenter); 1551 1552 // This part of the test checks if the SIP (Software Input Panel) is triggered, 1553 // which normally happens on mobile platforms, when a user input form receives 1554 // a mouse click. 1555 int inputPanel = 0; 1556 if (viewType == "QWebView") { 1557 if (QWebView* wv = qobject_cast<QWebView*>(view)) 1558 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); 1559 } else if (viewType == "QGraphicsWebView") { 1560 if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view)) 1561 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); 1562 } 1563 1564 // For non-mobile platforms RequestSoftwareInputPanel event is not called 1565 // because there is no SIP (Software Input Panel) triggered. In the case of a 1566 // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked 1567 // and the RequestSoftwareInputPanel event is called. For these two situations 1568 // this part of the test can verified as the checks below. 1569 if (inputPanel) 1570 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1571 else 1572 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1573 viewEventSpy.clear(); 1574 1575 clickOnPage(page, textInputCenter); 1576 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1577 1578 //ImMicroFocus 1579 QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus); 1580 QRect focusRect = variant.toRect(); 1581 QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft())); 1582 1583 //ImFont 1584 variant = page->inputMethodQuery(Qt::ImFont); 1585 QFont font = variant.value<QFont>(); 1586 QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family()); 1587 1588 QList<QInputMethodEvent::Attribute> inputAttributes; 1589 1590 //Insert text. 1591 { 1592 QInputMethodEvent eventText("QtWebKit", inputAttributes); 1593 QSignalSpy signalSpy(page, SIGNAL(microFocusChanged())); 1594 page->event(&eventText); 1595 QCOMPARE(signalSpy.count(), 0); 1596 } 1597 1598 { 1599 QInputMethodEvent eventText("", inputAttributes); 1600 eventText.setCommitString(QString("QtWebKit"), 0, 0); 1601 page->event(&eventText); 1602 } 1603 1604 //ImMaximumTextLength 1605 variant = page->inputMethodQuery(Qt::ImMaximumTextLength); 1606 QCOMPARE(20, variant.toInt()); 1607 1608 //Set selection 1609 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant()); 1610 QInputMethodEvent eventSelection("",inputAttributes); 1611 page->event(&eventSelection); 1612 1613 //ImAnchorPosition 1614 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1615 int anchorPosition = variant.toInt(); 1616 QCOMPARE(anchorPosition, 3); 1617 1618 //ImCursorPosition 1619 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1620 int cursorPosition = variant.toInt(); 1621 QCOMPARE(cursorPosition, 5); 1622 1623 //ImCurrentSelection 1624 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1625 QString selectionValue = variant.value<QString>(); 1626 QCOMPARE(selectionValue, QString("eb")); 1627 1628 //Set selection with negative length 1629 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant()); 1630 QInputMethodEvent eventSelection3("",inputAttributes); 1631 page->event(&eventSelection3); 1632 1633 //ImAnchorPosition 1634 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1635 anchorPosition = variant.toInt(); 1636 QCOMPARE(anchorPosition, 1); 1637 1638 //ImCursorPosition 1639 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1640 cursorPosition = variant.toInt(); 1641 QCOMPARE(cursorPosition, 6); 1642 1643 //ImCurrentSelection 1644 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1645 selectionValue = variant.value<QString>(); 1646 QCOMPARE(selectionValue, QString("tWebK")); 1647 1648 //ImSurroundingText 1649 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1650 QString value = variant.value<QString>(); 1651 QCOMPARE(value, QString("QtWebKit")); 1652 1653 { 1654 QList<QInputMethodEvent::Attribute> attributes; 1655 // Clear the selection, so the next test does not clear any contents. 1656 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); 1657 attributes.append(newSelection); 1658 QInputMethodEvent event("composition", attributes); 1659 page->event(&event); 1660 } 1661 1662 // A ongoing composition should not change the surrounding text before it is committed. 1663 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1664 value = variant.value<QString>(); 1665 QCOMPARE(value, QString("QtWebKit")); 1666 1667 // Cancel current composition first 1668 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant()); 1669 QInputMethodEvent eventSelection4("", inputAttributes); 1670 page->event(&eventSelection4); 1671 1672 // START - Tests for Selection when the Editor is NOT in Composition mode 1673 1674 // LEFT to RIGHT selection 1675 // Deselect the selection by sending MouseButtonPress events 1676 // This moves the current cursor to the end of the text 1677 clickOnPage(page, textInputCenter); 1678 1679 { 1680 QList<QInputMethodEvent::Attribute> attributes; 1681 QInputMethodEvent event(QString(), attributes); 1682 event.setCommitString("XXX", 0, 0); 1683 page->event(&event); 1684 event.setCommitString(QString(), -2, 2); // Erase two characters. 1685 page->event(&event); 1686 event.setCommitString(QString(), -1, 1); // Erase one character. 1687 page->event(&event); 1688 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1689 value = variant.value<QString>(); 1690 QCOMPARE(value, QString("QtWebKit")); 1691 } 1692 1693 //Move to the start of the line 1694 page->triggerAction(QWebPage::MoveToStartOfLine); 1695 1696 QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); 1697 QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier); 1698 1699 //Move 2 characters RIGHT 1700 for (int j = 0; j < 2; ++j) { 1701 page->event(&keyRightEventPress); 1702 page->event(&keyRightEventRelease); 1703 } 1704 1705 //Select to the end of the line 1706 page->triggerAction(QWebPage::SelectEndOfLine); 1707 1708 //ImAnchorPosition QtWebKit 1709 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1710 anchorPosition = variant.toInt(); 1711 QCOMPARE(anchorPosition, 2); 1712 1713 //ImCursorPosition 1714 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1715 cursorPosition = variant.toInt(); 1716 QCOMPARE(cursorPosition, 8); 1717 1718 //ImCurrentSelection 1719 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1720 selectionValue = variant.value<QString>(); 1721 QCOMPARE(selectionValue, QString("WebKit")); 1722 1723 //RIGHT to LEFT selection 1724 //Deselect the selection (this moves the current cursor to the end of the text) 1725 clickOnPage(page, textInputCenter); 1726 1727 //ImAnchorPosition 1728 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1729 anchorPosition = variant.toInt(); 1730 QCOMPARE(anchorPosition, 8); 1731 1732 //ImCursorPosition 1733 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1734 cursorPosition = variant.toInt(); 1735 QCOMPARE(cursorPosition, 8); 1736 1737 //ImCurrentSelection 1738 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1739 selectionValue = variant.value<QString>(); 1740 QCOMPARE(selectionValue, QString("")); 1741 1742 QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); 1743 QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier); 1744 1745 //Move 2 characters LEFT 1746 for (int i = 0; i < 2; ++i) { 1747 page->event(&keyLeftEventPress); 1748 page->event(&keyLeftEventRelease); 1749 } 1750 1751 //Select to the start of the line 1752 page->triggerAction(QWebPage::SelectStartOfLine); 1753 1754 //ImAnchorPosition 1755 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1756 anchorPosition = variant.toInt(); 1757 QCOMPARE(anchorPosition, 6); 1758 1759 //ImCursorPosition 1760 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1761 cursorPosition = variant.toInt(); 1762 QCOMPARE(cursorPosition, 0); 1763 1764 //ImCurrentSelection 1765 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1766 selectionValue = variant.value<QString>(); 1767 QCOMPARE(selectionValue, QString("QtWebK")); 1768 1769 //END - Tests for Selection when the Editor is not in Composition mode 1770 1771 //ImhHiddenText 1772 QPoint passwordInputCenter = inputs.at(1).geometry().center(); 1773 clickOnPage(page, passwordInputCenter); 1774 1775 QVERIFY(inputMethodEnabled(view)); 1776 QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText); 1777 1778 clickOnPage(page, textInputCenter); 1779 QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText)); 1780 1781 page->mainFrame()->setHtml("<html><body><p>nothing to input here"); 1782 viewEventSpy.clear(); 1783 1784 QWebElement para = page->mainFrame()->findFirstElement("p"); 1785 clickOnPage(page, para.geometry().center()); 1786 1787 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1788 1789 //START - Test for sending empty QInputMethodEvent 1790 page->mainFrame()->setHtml("<html><body>" \ 1791 "<input type='text' id='input3' value='QtWebKit2'/>" \ 1792 "</body></html>"); 1793 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();"); 1794 1795 //Send empty QInputMethodEvent 1796 QInputMethodEvent emptyEvent; 1797 page->event(&emptyEvent); 1798 1799 QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString(); 1800 QCOMPARE(inputValue, QString("QtWebKit2")); 1801 //END - Test for sending empty QInputMethodEvent 1802 1803 page->mainFrame()->setHtml("<html><body>" \ 1804 "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \ 1805 "</body></html>"); 1806 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();"); 1807 1808 // Clear the selection, also cancel the ongoing composition if there is one. 1809 { 1810 QList<QInputMethodEvent::Attribute> attributes; 1811 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); 1812 attributes.append(newSelection); 1813 QInputMethodEvent event("", attributes); 1814 page->event(&event); 1815 } 1816 1817 // ImCurrentSelection 1818 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1819 selectionValue = variant.value<QString>(); 1820 QCOMPARE(selectionValue, QString("")); 1821 1822 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1823 QString surroundingValue = variant.value<QString>(); 1824 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); 1825 1826 // ImAnchorPosition 1827 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1828 anchorPosition = variant.toInt(); 1829 QCOMPARE(anchorPosition, 0); 1830 1831 // ImCursorPosition 1832 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1833 cursorPosition = variant.toInt(); 1834 QCOMPARE(cursorPosition, 0); 1835 1836 // 1. Insert a character to the begining of the line. 1837 // Send temporary text, which makes the editor has composition 'm'. 1838 { 1839 QList<QInputMethodEvent::Attribute> attributes; 1840 QInputMethodEvent event("m", attributes); 1841 page->event(&event); 1842 } 1843 1844 // ImCurrentSelection 1845 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1846 selectionValue = variant.value<QString>(); 1847 QCOMPARE(selectionValue, QString("")); 1848 1849 // ImSurroundingText 1850 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1851 surroundingValue = variant.value<QString>(); 1852 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); 1853 1854 // ImCursorPosition 1855 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1856 cursorPosition = variant.toInt(); 1857 QCOMPARE(cursorPosition, 0); 1858 1859 // ImAnchorPosition 1860 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1861 anchorPosition = variant.toInt(); 1862 QCOMPARE(anchorPosition, 0); 1863 1864 // Send temporary text, which makes the editor has composition 'n'. 1865 { 1866 QList<QInputMethodEvent::Attribute> attributes; 1867 QInputMethodEvent event("n", attributes); 1868 page->event(&event); 1869 } 1870 1871 // ImCurrentSelection 1872 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1873 selectionValue = variant.value<QString>(); 1874 QCOMPARE(selectionValue, QString("")); 1875 1876 // ImSurroundingText 1877 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1878 surroundingValue = variant.value<QString>(); 1879 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); 1880 1881 // ImCursorPosition 1882 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1883 cursorPosition = variant.toInt(); 1884 QCOMPARE(cursorPosition, 0); 1885 1886 // ImAnchorPosition 1887 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1888 anchorPosition = variant.toInt(); 1889 QCOMPARE(anchorPosition, 0); 1890 1891 // Send commit text, which makes the editor conforms composition. 1892 { 1893 QList<QInputMethodEvent::Attribute> attributes; 1894 QInputMethodEvent event("", attributes); 1895 event.setCommitString("o"); 1896 page->event(&event); 1897 } 1898 1899 // ImCurrentSelection 1900 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1901 selectionValue = variant.value<QString>(); 1902 QCOMPARE(selectionValue, QString("")); 1903 1904 // ImSurroundingText 1905 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1906 surroundingValue = variant.value<QString>(); 1907 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod")); 1908 1909 // ImCursorPosition 1910 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1911 cursorPosition = variant.toInt(); 1912 QCOMPARE(cursorPosition, 1); 1913 1914 // ImAnchorPosition 1915 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1916 anchorPosition = variant.toInt(); 1917 QCOMPARE(anchorPosition, 1); 1918 1919 // 2. insert a character to the middle of the line. 1920 // Send temporary text, which makes the editor has composition 'd'. 1921 { 1922 QList<QInputMethodEvent::Attribute> attributes; 1923 QInputMethodEvent event("d", attributes); 1924 page->event(&event); 1925 } 1926 1927 // ImCurrentSelection 1928 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1929 selectionValue = variant.value<QString>(); 1930 QCOMPARE(selectionValue, QString("")); 1931 1932 // ImSurroundingText 1933 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1934 surroundingValue = variant.value<QString>(); 1935 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod")); 1936 1937 // ImCursorPosition 1938 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1939 cursorPosition = variant.toInt(); 1940 QCOMPARE(cursorPosition, 1); 1941 1942 // ImAnchorPosition 1943 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1944 anchorPosition = variant.toInt(); 1945 QCOMPARE(anchorPosition, 1); 1946 1947 // Send commit text, which makes the editor conforms composition. 1948 { 1949 QList<QInputMethodEvent::Attribute> attributes; 1950 QInputMethodEvent event("", attributes); 1951 event.setCommitString("e"); 1952 page->event(&event); 1953 } 1954 1955 // ImCurrentSelection 1956 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1957 selectionValue = variant.value<QString>(); 1958 QCOMPARE(selectionValue, QString("")); 1959 1960 // ImSurroundingText 1961 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1962 surroundingValue = variant.value<QString>(); 1963 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod")); 1964 1965 // ImCursorPosition 1966 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1967 cursorPosition = variant.toInt(); 1968 QCOMPARE(cursorPosition, 2); 1969 1970 // ImAnchorPosition 1971 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1972 anchorPosition = variant.toInt(); 1973 QCOMPARE(anchorPosition, 2); 1974 1975 // 3. Insert a character to the end of the line. 1976 page->triggerAction(QWebPage::MoveToEndOfLine); 1977 1978 // Send temporary text, which makes the editor has composition 't'. 1979 { 1980 QList<QInputMethodEvent::Attribute> attributes; 1981 QInputMethodEvent event("t", attributes); 1982 page->event(&event); 1983 } 1984 1985 // ImCurrentSelection 1986 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1987 selectionValue = variant.value<QString>(); 1988 QCOMPARE(selectionValue, QString("")); 1989 1990 // ImSurroundingText 1991 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1992 surroundingValue = variant.value<QString>(); 1993 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod")); 1994 1995 // ImCursorPosition 1996 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1997 cursorPosition = variant.toInt(); 1998 QCOMPARE(cursorPosition, 22); 1999 2000 // ImAnchorPosition 2001 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 2002 anchorPosition = variant.toInt(); 2003 QCOMPARE(anchorPosition, 22); 2004 2005 // Send commit text, which makes the editor conforms composition. 2006 { 2007 QList<QInputMethodEvent::Attribute> attributes; 2008 QInputMethodEvent event("", attributes); 2009 event.setCommitString("t"); 2010 page->event(&event); 2011 } 2012 2013 // ImCurrentSelection 2014 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 2015 selectionValue = variant.value<QString>(); 2016 QCOMPARE(selectionValue, QString("")); 2017 2018 // ImSurroundingText 2019 variant = page->inputMethodQuery(Qt::ImSurroundingText); 2020 surroundingValue = variant.value<QString>(); 2021 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt")); 2022 2023 // ImCursorPosition 2024 variant = page->inputMethodQuery(Qt::ImCursorPosition); 2025 cursorPosition = variant.toInt(); 2026 QCOMPARE(cursorPosition, 23); 2027 2028 // ImAnchorPosition 2029 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 2030 anchorPosition = variant.toInt(); 2031 QCOMPARE(anchorPosition, 23); 2032 2033 // 4. Replace the selection. 2034 page->triggerAction(QWebPage::SelectPreviousWord); 2035 2036 // ImCurrentSelection 2037 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 2038 selectionValue = variant.value<QString>(); 2039 QCOMPARE(selectionValue, QString("inputMethodt")); 2040 2041 // ImSurroundingText 2042 variant = page->inputMethodQuery(Qt::ImSurroundingText); 2043 surroundingValue = variant.value<QString>(); 2044 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt")); 2045 2046 // ImCursorPosition 2047 variant = page->inputMethodQuery(Qt::ImCursorPosition); 2048 cursorPosition = variant.toInt(); 2049 QCOMPARE(cursorPosition, 11); 2050 2051 // ImAnchorPosition 2052 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 2053 anchorPosition = variant.toInt(); 2054 QCOMPARE(anchorPosition, 23); 2055 2056 // Send temporary text, which makes the editor has composition 'w'. 2057 { 2058 QList<QInputMethodEvent::Attribute> attributes; 2059 QInputMethodEvent event("w", attributes); 2060 page->event(&event); 2061 } 2062 2063 // ImCurrentSelection 2064 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 2065 selectionValue = variant.value<QString>(); 2066 QCOMPARE(selectionValue, QString("")); 2067 2068 // ImSurroundingText 2069 variant = page->inputMethodQuery(Qt::ImSurroundingText); 2070 surroundingValue = variant.value<QString>(); 2071 QCOMPARE(surroundingValue, QString("oeQtWebKit ")); 2072 2073 // ImCursorPosition 2074 variant = page->inputMethodQuery(Qt::ImCursorPosition); 2075 cursorPosition = variant.toInt(); 2076 QCOMPARE(cursorPosition, 11); 2077 2078 // ImAnchorPosition 2079 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 2080 anchorPosition = variant.toInt(); 2081 QCOMPARE(anchorPosition, 11); 2082 2083 // Send commit text, which makes the editor conforms composition. 2084 { 2085 QList<QInputMethodEvent::Attribute> attributes; 2086 QInputMethodEvent event("", attributes); 2087 event.setCommitString("2"); 2088 page->event(&event); 2089 } 2090 2091 // ImCurrentSelection 2092 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 2093 selectionValue = variant.value<QString>(); 2094 QCOMPARE(selectionValue, QString("")); 2095 2096 // ImSurroundingText 2097 variant = page->inputMethodQuery(Qt::ImSurroundingText); 2098 surroundingValue = variant.value<QString>(); 2099 QCOMPARE(surroundingValue, QString("oeQtWebKit 2")); 2100 2101 // ImCursorPosition 2102 variant = page->inputMethodQuery(Qt::ImCursorPosition); 2103 cursorPosition = variant.toInt(); 2104 QCOMPARE(cursorPosition, 12); 2105 2106 // ImAnchorPosition 2107 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 2108 anchorPosition = variant.toInt(); 2109 QCOMPARE(anchorPosition, 12); 2110 2111 // Check sending RequestSoftwareInputPanel event 2112 page->mainFrame()->setHtml("<html><body>" \ 2113 "<input type='text' id='input5' value='QtWebKit inputMethod'/>" \ 2114 "<div id='btnDiv' onclick='i=document.getElementById("input5"); i.focus();'>abc</div>"\ 2115 "</body></html>"); 2116 QWebElement inputElement = page->mainFrame()->findFirstElement("div"); 2117 clickOnPage(page, inputElement.geometry().center()); 2118 2119 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 2120 delete container; 2121} 2122 2123void tst_QWebPage::inputMethodsTextFormat_data() 2124{ 2125 QTest::addColumn<QString>("string"); 2126 QTest::addColumn<int>("start"); 2127 QTest::addColumn<int>("length"); 2128 2129 QTest::newRow("") << QString("") << 0 << 0; 2130 QTest::newRow("Q") << QString("Q") << 0 << 1; 2131 QTest::newRow("Qt") << QString("Qt") << 0 << 1; 2132 QTest::newRow("Qt") << QString("Qt") << 0 << 2; 2133 QTest::newRow("Qt") << QString("Qt") << 1 << 1; 2134 QTest::newRow("Qt ") << QString("Qt ") << 0 << 1; 2135 QTest::newRow("Qt ") << QString("Qt ") << 1 << 1; 2136 QTest::newRow("Qt ") << QString("Qt ") << 2 << 1; 2137 QTest::newRow("Qt ") << QString("Qt ") << 2 << -1; 2138 QTest::newRow("Qt ") << QString("Qt ") << -2 << 3; 2139 QTest::newRow("Qt ") << QString("Qt ") << 0 << 3; 2140 QTest::newRow("Qt by") << QString("Qt by") << 0 << 1; 2141 QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1; 2142} 2143 2144 2145void tst_QWebPage::inputMethodsTextFormat() 2146{ 2147 QWebPage* page = new QWebPage; 2148 QWebView* view = new QWebView; 2149 view->setPage(page); 2150 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont"); 2151 page->mainFrame()->setHtml("<html><body>" \ 2152 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>"); 2153 page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()"); 2154 page->mainFrame()->setFocus(); 2155 view->show(); 2156 2157 QFETCH(QString, string); 2158 QFETCH(int, start); 2159 QFETCH(int, length); 2160 2161 QList<QInputMethodEvent::Attribute> attrs; 2162 QTextCharFormat format; 2163 format.setUnderlineStyle(QTextCharFormat::SingleUnderline); 2164 format.setUnderlineColor(Qt::red); 2165 attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format)); 2166 QInputMethodEvent im(string, attrs); 2167 page->event(&im); 2168 2169 QTest::qWait(1000); 2170 2171 delete view; 2172} 2173 2174void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector() 2175{ 2176 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); 2177 2178 PluginPage* newPage = new PluginPage(m_view); 2179 m_view->setPage(newPage); 2180 2181 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 2182 2183 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>")); 2184 QTRY_COMPARE(loadSpy.count(), 1); 2185 2186 newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }"); 2187 2188 newPage->mainFrame()->evaluateJavaScript("testme('foo')"); 2189 2190 DumpRenderTreeSupportQt::garbageCollectorCollect(); 2191 2192 // don't crash! 2193 newPage->mainFrame()->evaluateJavaScript("testme('bar')"); 2194} 2195 2196void tst_QWebPage::localURLSchemes() 2197{ 2198 int i = QWebSecurityOrigin::localSchemes().size(); 2199 2200 QWebSecurityOrigin::removeLocalScheme("file"); 2201 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 2202 QWebSecurityOrigin::addLocalScheme("file"); 2203 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 2204 2205 QWebSecurityOrigin::removeLocalScheme("qrc"); 2206 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1); 2207 QWebSecurityOrigin::addLocalScheme("qrc"); 2208 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 2209 2210 QString myscheme = "myscheme"; 2211 QWebSecurityOrigin::addLocalScheme(myscheme); 2212 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1); 2213 QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme)); 2214 QWebSecurityOrigin::removeLocalScheme(myscheme); 2215 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 2216 QWebSecurityOrigin::removeLocalScheme(myscheme); 2217 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 2218} 2219 2220static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue) 2221{ 2222 webPage.settings()->setAttribute(settingAttribute, settingValue); 2223 return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool(); 2224} 2225 2226void tst_QWebPage::testOptionalJSObjects() 2227{ 2228 // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off 2229 // the visibility of the JS object any more. For this reason this test uses two QWebPage instances. 2230 // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on 2231 // a feature for one instance will not turn it on for another. 2232 2233 QWebPage webPage1; 2234 QWebPage webPage2; 2235 2236 webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); 2237 webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); 2238 2239 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); 2240 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); 2241 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true); 2242 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); 2243 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); 2244 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true); 2245 2246 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); 2247 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true); 2248 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); 2249 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true); 2250} 2251 2252void tst_QWebPage::testEnablePersistentStorage() 2253{ 2254 QWebPage webPage; 2255 2256 // By default all persistent options should be disabled 2257 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false); 2258 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false); 2259 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false); 2260 QVERIFY(webPage.settings()->iconDatabasePath().isEmpty()); 2261 2262 QWebSettings::enablePersistentStorage(); 2263 2264 2265 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true); 2266 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true); 2267 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true); 2268 2269 QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty()); 2270 QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty()); 2271 QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty()); 2272} 2273 2274void tst_QWebPage::defaultTextEncoding() 2275{ 2276 QWebFrame* mainFrame = m_page->mainFrame(); 2277 2278 QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 2279 QVERIFY(!defaultCharset.isEmpty()); 2280 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset); 2281 2282 m_page->settings()->setDefaultTextEncoding(QString("utf-8")); 2283 QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 2284 QCOMPARE(charset, QString("utf-8")); 2285 QCOMPARE(m_page->settings()->defaultTextEncoding(), charset); 2286 2287 m_page->settings()->setDefaultTextEncoding(QString()); 2288 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 2289 QVERIFY(!charset.isEmpty()); 2290 QCOMPARE(charset, defaultCharset); 2291 2292 QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8")); 2293 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 2294 QCOMPARE(charset, QString("utf-8")); 2295 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset); 2296} 2297 2298class ErrorPage : public QWebPage 2299{ 2300public: 2301 2302 ErrorPage(QWidget* parent = 0): QWebPage(parent) 2303 { 2304 } 2305 2306 virtual bool supportsExtension(Extension extension) const 2307 { 2308 return extension == ErrorPageExtension; 2309 } 2310 2311 virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output) 2312 { 2313 ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output); 2314 2315 errorPage->contentType = "text/html"; 2316 errorPage->content = "error"; 2317 return true; 2318 } 2319}; 2320 2321void tst_QWebPage::errorPageExtension() 2322{ 2323 ErrorPage* page = new ErrorPage; 2324 m_view->setPage(page); 2325 2326 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 2327 2328 m_view->setUrl(QUrl("data:text/html,foo")); 2329 QTRY_COMPARE(spyLoadFinished.count(), 1); 2330 2331 page->mainFrame()->setUrl(QUrl("http://non.existent/url")); 2332 QTRY_COMPARE(spyLoadFinished.count(), 2); 2333 QCOMPARE(page->mainFrame()->toPlainText(), QString("error")); 2334 QCOMPARE(page->history()->count(), 2); 2335 QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url")); 2336 QCOMPARE(page->history()->canGoBack(), true); 2337 QCOMPARE(page->history()->canGoForward(), false); 2338 2339 page->triggerAction(QWebPage::Back); 2340 QTRY_COMPARE(page->history()->canGoBack(), false); 2341 QTRY_COMPARE(page->history()->canGoForward(), true); 2342 2343 page->triggerAction(QWebPage::Forward); 2344 QTRY_COMPARE(page->history()->canGoBack(), true); 2345 QTRY_COMPARE(page->history()->canGoForward(), false); 2346 2347 page->triggerAction(QWebPage::Back); 2348 QTRY_COMPARE(page->history()->canGoBack(), false); 2349 QTRY_COMPARE(page->history()->canGoForward(), true); 2350 QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo")); 2351 2352 m_view->setPage(0); 2353} 2354 2355void tst_QWebPage::errorPageExtensionInIFrames() 2356{ 2357 ErrorPage* page = new ErrorPage; 2358 m_view->setPage(page); 2359 2360 m_view->page()->mainFrame()->load(QUrl( 2361 "data:text/html," 2362 "<h1>h1</h1>" 2363 "<iframe src='data:text/html,<p/>p'></iframe>" 2364 "<iframe src='http://non.existent/url'></iframe>")); 2365 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 2366 QTRY_COMPARE(spyLoadFinished.count(), 1); 2367 2368 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error")); 2369 2370 m_view->setPage(0); 2371} 2372 2373void tst_QWebPage::errorPageExtensionInFrameset() 2374{ 2375 ErrorPage* page = new ErrorPage; 2376 m_view->setPage(page); 2377 2378 m_view->load(QUrl("qrc:///resources/index.html")); 2379 2380 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 2381 QTRY_COMPARE(spyLoadFinished.count(), 1); 2382 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error")); 2383 2384 m_view->setPage(0); 2385} 2386 2387class FriendlyWebPage : public QWebPage 2388{ 2389public: 2390 friend class tst_QWebPage; 2391}; 2392 2393void tst_QWebPage::userAgentApplicationName() 2394{ 2395 const QString oldApplicationName = QCoreApplication::applicationName(); 2396 FriendlyWebPage page; 2397 2398 const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236"); 2399 QCoreApplication::setApplicationName(applicationNameMarker); 2400 QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker)); 2401 2402 QCoreApplication::setApplicationName(oldApplicationName); 2403} 2404 2405void tst_QWebPage::crashTests_LazyInitializationOfMainFrame() 2406{ 2407 { 2408 QWebPage webPage; 2409 } 2410 { 2411 QWebPage webPage; 2412 webPage.selectedText(); 2413 } 2414 { 2415 QWebPage webPage; 2416 webPage.selectedHtml(); 2417 } 2418 { 2419 QWebPage webPage; 2420 webPage.triggerAction(QWebPage::Back, true); 2421 } 2422 { 2423 QWebPage webPage; 2424 QPoint pos(10,10); 2425 webPage.updatePositionDependentActions(pos); 2426 } 2427} 2428 2429static void takeScreenshot(QWebPage* page) 2430{ 2431 QWebFrame* mainFrame = page->mainFrame(); 2432 page->setViewportSize(mainFrame->contentsSize()); 2433 QImage image(page->viewportSize(), QImage::Format_ARGB32); 2434 QPainter painter(&image); 2435 mainFrame->render(&painter); 2436 painter.end(); 2437} 2438 2439void tst_QWebPage::screenshot_data() 2440{ 2441 QTest::addColumn<QString>("html"); 2442 QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; 2443 QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); 2444 QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>"); 2445} 2446 2447void tst_QWebPage::screenshot() 2448{ 2449 if (!QDir(TESTS_SOURCE_DIR).exists()) 2450 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); 2451 2452 QDir::setCurrent(TESTS_SOURCE_DIR); 2453 2454 QFETCH(QString, html); 2455 QWebPage* page = new QWebPage; 2456 page->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 2457 QWebFrame* mainFrame = page->mainFrame(); 2458 mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); 2459 ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000); 2460 2461 // take screenshot without a view 2462 takeScreenshot(page); 2463 2464 QWebView* view = new QWebView; 2465 view->setPage(page); 2466 2467 // take screenshot when attached to a view 2468 takeScreenshot(page); 2469 2470 delete page; 2471 delete view; 2472 2473 QDir::setCurrent(QApplication::applicationDirPath()); 2474} 2475 2476void tst_QWebPage::originatingObjectInNetworkRequests() 2477{ 2478 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 2479 m_page->setNetworkAccessManager(networkManager); 2480 networkManager->requests.clear(); 2481 2482 m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 2483 "<head><meta http-equiv='refresh' content='1'></head>foo \">" 2484 "<frame src=\"data:text/html,bar\"></frameset>"), QUrl()); 2485 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 2486 2487 QCOMPARE(networkManager->requests.count(), 2); 2488 2489 QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames(); 2490 QCOMPARE(childFrames.count(), 2); 2491 2492 for (int i = 0; i < 2; ++i) 2493 QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i)); 2494} 2495 2496/** 2497 * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914 2498 * 2499 * From JS we test the following conditions. 2500 * 2501 * OK + QString() => SUCCESS, empty string (but not null) 2502 * OK + "text" => SUCCESS, "text" 2503 * CANCEL + QString() => CANCEL, null string 2504 * CANCEL + "text" => CANCEL, null string 2505 */ 2506class JSPromptPage : public QWebPage { 2507 Q_OBJECT 2508public: 2509 JSPromptPage() 2510 {} 2511 2512 bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result) 2513 { 2514 if (msg == QLatin1String("test1")) { 2515 *result = QString(); 2516 return true; 2517 } else if (msg == QLatin1String("test2")) { 2518 *result = QLatin1String("text"); 2519 return true; 2520 } else if (msg == QLatin1String("test3")) { 2521 *result = QString(); 2522 return false; 2523 } else if (msg == QLatin1String("test4")) { 2524 *result = QLatin1String("text"); 2525 return false; 2526 } 2527 2528 qFatal("Unknown msg."); 2529 return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result); 2530 } 2531}; 2532 2533void tst_QWebPage::testJSPrompt() 2534{ 2535 JSPromptPage page; 2536 bool res; 2537 2538 // OK + QString() 2539 res = page.mainFrame()->evaluateJavaScript( 2540 "var retval = prompt('test1');" 2541 "retval=='' && retval.length == 0;").toBool(); 2542 QVERIFY(res); 2543 2544 // OK + "text" 2545 res = page.mainFrame()->evaluateJavaScript( 2546 "var retval = prompt('test2');" 2547 "retval=='text' && retval.length == 4;").toBool(); 2548 QVERIFY(res); 2549 2550 // Cancel + QString() 2551 res = page.mainFrame()->evaluateJavaScript( 2552 "var retval = prompt('test3');" 2553 "retval===null;").toBool(); 2554 QVERIFY(res); 2555 2556 // Cancel + "text" 2557 res = page.mainFrame()->evaluateJavaScript( 2558 "var retval = prompt('test4');" 2559 "retval===null;").toBool(); 2560 QVERIFY(res); 2561} 2562 2563class TestModalPage : public QWebPage 2564{ 2565 Q_OBJECT 2566public: 2567 TestModalPage(QObject* parent = 0) : QWebPage(parent) { 2568 } 2569 virtual QWebPage* createWindow(WebWindowType) { 2570 QWebPage* page = new TestModalPage(); 2571 connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater())); 2572 return page; 2573 } 2574}; 2575 2576void tst_QWebPage::showModalDialog() 2577{ 2578 TestModalPage page; 2579 page.mainFrame()->setHtml(QString("<html></html>")); 2580 QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString(); 2581 QCOMPARE(res, QString("This is a test")); 2582} 2583 2584void tst_QWebPage::testStopScheduledPageRefresh() 2585{ 2586 // Without QWebPage::StopScheduledPageRefresh 2587 QWebPage page1; 2588 page1.setNetworkAccessManager(new TestNetworkManager(&page1)); 2589 page1.mainFrame()->setHtml("<html><head>" 2590 "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">" 2591 "</head><body><h1>Page redirects immediately...</h1>" 2592 "</body></html>"); 2593 QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool)))); 2594 QTest::qWait(500); 2595 QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html"))); 2596 2597 // With QWebPage::StopScheduledPageRefresh 2598 QWebPage page2; 2599 page2.setNetworkAccessManager(new TestNetworkManager(&page2)); 2600 page2.mainFrame()->setHtml("<html><head>" 2601 "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">" 2602 "</head><body><h1>Page redirect test with 1 sec timeout...</h1>" 2603 "</body></html>"); 2604 page2.triggerAction(QWebPage::StopScheduledPageRefresh); 2605 QTest::qWait(1500); 2606 QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank")); 2607} 2608 2609void tst_QWebPage::findText() 2610{ 2611 m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); 2612 m_page->triggerAction(QWebPage::SelectAll); 2613 QVERIFY(!m_page->selectedText().isEmpty()); 2614 QVERIFY(!m_page->selectedHtml().isEmpty()); 2615 m_page->findText(""); 2616 QVERIFY(m_page->selectedText().isEmpty()); 2617 QVERIFY(m_page->selectedHtml().isEmpty()); 2618 QStringList words = (QStringList() << "foo" << "bar"); 2619 foreach (QString subString, words) { 2620 m_page->findText(subString, QWebPage::FindWrapsAroundDocument); 2621 QCOMPARE(m_page->selectedText(), subString); 2622 QCOMPARE(m_page->selectedHtml(), QString("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \">%1</span>").arg(subString)); 2623 m_page->findText(""); 2624 QVERIFY(m_page->selectedText().isEmpty()); 2625 QVERIFY(m_page->selectedHtml().isEmpty()); 2626 } 2627} 2628 2629struct ImageExtensionMap { 2630 const char* extension; 2631 const char* mimeType; 2632}; 2633 2634static const ImageExtensionMap extensionMap[] = { 2635 { "bmp", "image/bmp" }, 2636 { "css", "text/css" }, 2637 { "gif", "image/gif" }, 2638 { "html", "text/html" }, 2639 { "htm", "text/html" }, 2640 { "ico", "image/x-icon" }, 2641 { "jpeg", "image/jpeg" }, 2642 { "jpg", "image/jpeg" }, 2643 { "js", "application/x-javascript" }, 2644 { "mng", "video/x-mng" }, 2645 { "pbm", "image/x-portable-bitmap" }, 2646 { "pgm", "image/x-portable-graymap" }, 2647 { "pdf", "application/pdf" }, 2648 { "png", "image/png" }, 2649 { "ppm", "image/x-portable-pixmap" }, 2650 { "rss", "application/rss+xml" }, 2651 { "svg", "image/svg+xml" }, 2652 { "text", "text/plain" }, 2653 { "tif", "image/tiff" }, 2654 { "tiff", "image/tiff" }, 2655 { "txt", "text/plain" }, 2656 { "xbm", "image/x-xbitmap" }, 2657 { "xml", "text/xml" }, 2658 { "xpm", "image/x-xpm" }, 2659 { "xsl", "text/xsl" }, 2660 { "xhtml", "application/xhtml+xml" }, 2661 { "wml", "text/vnd.wap.wml" }, 2662 { "wmlc", "application/vnd.wap.wmlc" }, 2663 { 0, 0 } 2664}; 2665 2666static QString getMimeTypeForExtension(const QString &ext) 2667{ 2668 const ImageExtensionMap *e = extensionMap; 2669 while (e->extension) { 2670 if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0) 2671 return QLatin1String(e->mimeType); 2672 ++e; 2673 } 2674 2675 return QString(); 2676} 2677 2678void tst_QWebPage::supportedContentType() 2679{ 2680 QStringList contentTypes; 2681 2682 // Add supported non image types... 2683 contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/" 2684 << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml" 2685 << "application/rss+xml" << "application/atom+xml" << "application/json"; 2686 2687 // Add supported image types... 2688 Q_FOREACH(const QByteArray& imageType, QImageWriter::supportedImageFormats()) { 2689 const QString mimeType = getMimeTypeForExtension(imageType); 2690 if (!mimeType.isEmpty()) 2691 contentTypes << mimeType; 2692 } 2693 2694 // Get the mime types supported by webkit... 2695 const QStringList supportedContentTypes = m_page->supportedContentTypes(); 2696 2697 Q_FOREACH(const QString& mimeType, contentTypes) 2698 QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1()); 2699 2700 Q_FOREACH(const QString& mimeType, contentTypes) 2701 QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1()); 2702} 2703 2704 2705void tst_QWebPage::navigatorCookieEnabled() 2706{ 2707 m_page->networkAccessManager()->setCookieJar(0); 2708 QVERIFY(!m_page->networkAccessManager()->cookieJar()); 2709 QVERIFY(!m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool()); 2710 2711 m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); 2712 QVERIFY(m_page->networkAccessManager()->cookieJar()); 2713 QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool()); 2714} 2715 2716#ifdef Q_OS_MAC 2717void tst_QWebPage::macCopyUnicodeToClipboard() 2718{ 2719 QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ"); 2720 m_page->mainFrame()->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText)); 2721 m_page->triggerAction(QWebPage::SelectAll); 2722 m_page->triggerAction(QWebPage::Copy); 2723 2724 QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html"))); 2725 2726 QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"))); 2727 QVERIFY(clipboardData.contains(unicodeText)); 2728} 2729#endif 2730 2731void tst_QWebPage::contextMenuCopy() 2732{ 2733 QWebView view; 2734 2735 view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>"); 2736 2737 view.page()->triggerAction(QWebPage::SelectAll); 2738 QVERIFY(!view.page()->selectedText().isEmpty()); 2739 2740 QWebElement link = view.page()->mainFrame()->findFirstElement("a"); 2741 QPoint pos(link.geometry().center()); 2742 QContextMenuEvent event(QContextMenuEvent::Mouse, pos); 2743 view.page()->swallowContextMenuEvent(&event); 2744 view.page()->updatePositionDependentActions(pos); 2745 2746 QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); 2747 QVERIFY(!contextMenus.isEmpty()); 2748 QMenu* contextMenu = contextMenus.first(); 2749 QVERIFY(contextMenu); 2750 2751 QList<QAction *> list = contextMenu->actions(); 2752 int index = list.indexOf(view.page()->action(QWebPage::Copy)); 2753 QVERIFY(index != -1); 2754} 2755 2756void tst_QWebPage::deleteQWebViewTwice() 2757{ 2758 for (int i = 0; i < 2; ++i) { 2759 QMainWindow mainWindow; 2760 QWebView* webView = new QWebView(&mainWindow); 2761 mainWindow.setCentralWidget(webView); 2762 webView->load(QUrl("qrc:///resources/frame_a.html")); 2763 mainWindow.show(); 2764 connect(webView, SIGNAL(loadFinished(bool)), &mainWindow, SLOT(close())); 2765 QApplication::instance()->exec(); 2766 } 2767} 2768 2769QTEST_MAIN(tst_QWebPage) 2770#include "tst_qwebpage.moc" 2771