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