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