1 /* 2 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 3 Copyright (C) 2009 Girish Ramakrishnan <girish (at) forwardbias.in> 4 Copyright (C) 2010 Holger Hans Peter Freyther 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Library General Public 8 License as published by the Free Software Foundation; either 9 version 2 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Library General Public License for more details. 15 16 You should have received a copy of the GNU Library General Public License 17 along with this library; see the file COPYING.LIB. If not, write to 18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 #include "../util.h" 23 #include <QDir> 24 #include <QGraphicsWidget> 25 #include <QLineEdit> 26 #include <QMenu> 27 #include <QPushButton> 28 #include <QtTest/QtTest> 29 #include <qgraphicsscene.h> 30 #include <qgraphicsview.h> 31 #include <qgraphicswebview.h> 32 #include <qnetworkrequest.h> 33 #include <qwebdatabase.h> 34 #include <qwebelement.h> 35 #include <qwebframe.h> 36 #include <qwebhistory.h> 37 #include <qwebpage.h> 38 #include <qwebsecurityorigin.h> 39 #include <qwebview.h> 40 41 class EventSpy : public QObject, public QList<QEvent::Type> 42 { 43 Q_OBJECT 44 public: 45 EventSpy(QObject* objectToSpy) 46 { 47 objectToSpy->installEventFilter(this); 48 } 49 50 virtual bool eventFilter(QObject* receiver, QEvent* event) 51 { 52 append(event->type()); 53 return false; 54 } 55 }; 56 57 class tst_QWebPage : public QObject 58 { 59 Q_OBJECT 60 61 public: 62 tst_QWebPage(); 63 virtual ~tst_QWebPage(); 64 65 public slots: 66 void init(); 67 void cleanup(); 68 void cleanupFiles(); 69 70 private slots: 71 void initTestCase(); 72 void cleanupTestCase(); 73 74 void acceptNavigationRequest(); 75 void infiniteLoopJS(); 76 void loadFinished(); 77 void acceptNavigationRequestWithNewWindow(); 78 void userStyleSheet(); 79 void modified(); 80 void contextMenuCrash(); 81 void database(); 82 void createPlugin(); 83 void destroyPlugin_data(); 84 void destroyPlugin(); 85 void createViewlessPlugin_data(); 86 void createViewlessPlugin(); 87 void multiplePageGroupsAndLocalStorage(); 88 void cursorMovements(); 89 void textSelection(); 90 void textEditing(); 91 void backActionUpdate(); 92 void frameAt(); 93 void requestCache(); 94 void protectBindingsRuntimeObjectsFromCollector(); 95 void localURLSchemes(); 96 void testOptionalJSObjects(); 97 void testEnablePersistentStorage(); 98 void consoleOutput(); 99 void inputMethods_data(); 100 void inputMethods(); 101 void defaultTextEncoding(); 102 void errorPageExtension(); 103 void errorPageExtensionInIFrames(); 104 void errorPageExtensionInFrameset(); 105 106 void crashTests_LazyInitializationOfMainFrame(); 107 108 void screenshot_data(); 109 void screenshot(); 110 111 void originatingObjectInNetworkRequests(); 112 void testJSPrompt(); 113 void showModalDialog(); 114 115 private: 116 QWebView* m_view; 117 QWebPage* m_page; 118 }; 119 120 tst_QWebPage::tst_QWebPage() 121 { 122 } 123 124 tst_QWebPage::~tst_QWebPage() 125 { 126 } 127 128 void tst_QWebPage::init() 129 { 130 m_view = new QWebView(); 131 m_page = m_view->page(); 132 } 133 134 void tst_QWebPage::cleanup() 135 { 136 delete m_view; 137 } 138 139 void tst_QWebPage::cleanupFiles() 140 { 141 QFile::remove("Databases.db"); 142 QDir::current().rmdir("http_www.myexample.com_0"); 143 QFile::remove("http_www.myexample.com_0.localstorage"); 144 } 145 146 void tst_QWebPage::initTestCase() 147 { 148 cleanupFiles(); // In case there are old files from previous runs 149 } 150 151 void tst_QWebPage::cleanupTestCase() 152 { 153 cleanupFiles(); // Be nice 154 } 155 156 class NavigationRequestOverride : public QWebPage 157 { 158 public: 159 NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {} 160 161 bool m_acceptNavigationRequest; 162 protected: 163 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) { 164 Q_UNUSED(frame); 165 Q_UNUSED(request); 166 Q_UNUSED(type); 167 168 return m_acceptNavigationRequest; 169 } 170 }; 171 172 void tst_QWebPage::acceptNavigationRequest() 173 { 174 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); 175 176 NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false); 177 m_view->setPage(newPage); 178 179 m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>" 180 "<input type='text'><input type='submit'></form></body></html>"), QUrl()); 181 QTRY_COMPARE(loadSpy.count(), 1); 182 183 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); 184 185 newPage->m_acceptNavigationRequest = true; 186 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); 187 QTRY_COMPARE(loadSpy.count(), 2); 188 189 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?")); 190 191 // Restore default page 192 m_view->setPage(0); 193 } 194 195 class JSTestPage : public QWebPage 196 { 197 Q_OBJECT 198 public: 199 JSTestPage(QObject* parent = 0) 200 : QWebPage(parent) {} 201 202 public slots: 203 bool shouldInterruptJavaScript() { 204 return true; 205 } 206 }; 207 208 void tst_QWebPage::infiniteLoopJS() 209 { 210 JSTestPage* newPage = new JSTestPage(m_view); 211 m_view->setPage(newPage); 212 m_view->setHtml(QString("<html><bodytest</body></html>"), QUrl()); 213 m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}"); 214 } 215 216 void tst_QWebPage::loadFinished() 217 { 218 qRegisterMetaType<QWebFrame*>("QWebFrame*"); 219 qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*"); 220 QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted())); 221 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 222 223 m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 224 "<head><meta http-equiv='refresh' content='1'></head>foo \">" 225 "<frame src=\"data:text/html,bar\"></frameset>"), QUrl()); 226 QTRY_COMPARE(spyLoadFinished.count(), 1); 227 228 QTRY_VERIFY(spyLoadStarted.count() > 1); 229 QTRY_VERIFY(spyLoadFinished.count() > 1); 230 231 spyLoadFinished.clear(); 232 233 m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 234 "foo \"><frame src=\"data:text/html,bar\"></frameset>"), QUrl()); 235 QTRY_COMPARE(spyLoadFinished.count(), 1); 236 QCOMPARE(spyLoadFinished.count(), 1); 237 } 238 239 class ConsolePage : public QWebPage 240 { 241 public: 242 ConsolePage(QObject* parent = 0) : QWebPage(parent) {} 243 244 virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) 245 { 246 messages.append(message); 247 lineNumbers.append(lineNumber); 248 sourceIDs.append(sourceID); 249 } 250 251 QStringList messages; 252 QList<int> lineNumbers; 253 QStringList sourceIDs; 254 }; 255 256 void tst_QWebPage::consoleOutput() 257 { 258 ConsolePage page; 259 page.mainFrame()->evaluateJavaScript("this is not valid JavaScript"); 260 QCOMPARE(page.messages.count(), 1); 261 QCOMPARE(page.lineNumbers.at(0), 1); 262 } 263 264 class TestPage : public QWebPage 265 { 266 public: 267 TestPage(QObject* parent = 0) : QWebPage(parent) {} 268 269 struct Navigation { 270 QPointer<QWebFrame> frame; 271 QNetworkRequest request; 272 NavigationType type; 273 }; 274 275 QList<Navigation> navigations; 276 QList<QWebPage*> createdWindows; 277 278 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) { 279 Navigation n; 280 n.frame = frame; 281 n.request = request; 282 n.type = type; 283 navigations.append(n); 284 return true; 285 } 286 287 virtual QWebPage* createWindow(WebWindowType) { 288 QWebPage* page = new TestPage(this); 289 createdWindows.append(page); 290 return page; 291 } 292 }; 293 294 void tst_QWebPage::acceptNavigationRequestWithNewWindow() 295 { 296 TestPage* page = new TestPage(m_view); 297 page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true); 298 m_page = page; 299 m_view->setPage(m_page); 300 301 m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); 302 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 303 304 QFocusEvent fe(QEvent::FocusIn); 305 m_page->event(&fe); 306 307 QVERIFY(m_page->focusNextPrevChild(/*next*/ true)); 308 309 QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); 310 m_page->event(&keyEnter); 311 312 QCOMPARE(page->navigations.count(), 2); 313 314 TestPage::Navigation n = page->navigations.at(1); 315 QVERIFY(n.frame.isNull()); 316 QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached")); 317 QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked); 318 319 QCOMPARE(page->createdWindows.count(), 1); 320 } 321 322 class TestNetworkManager : public QNetworkAccessManager 323 { 324 public: 325 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} 326 327 QList<QUrl> requestedUrls; 328 QList<QNetworkRequest> requests; 329 330 protected: 331 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { 332 requests.append(request); 333 requestedUrls.append(request.url()); 334 return QNetworkAccessManager::createRequest(op, request, outgoingData); 335 } 336 }; 337 338 void tst_QWebPage::userStyleSheet() 339 { 340 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 341 m_page->setNetworkAccessManager(networkManager); 342 networkManager->requestedUrls.clear(); 343 344 m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," 345 + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); 346 m_view->setHtml("<p>hello world</p>"); 347 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 348 349 QVERIFY(networkManager->requestedUrls.count() >= 1); 350 QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); 351 } 352 353 void tst_QWebPage::modified() 354 { 355 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub")); 356 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 357 358 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah")); 359 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 360 361 QVERIFY(!m_page->isModified()); 362 363 // m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))"); 364 m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()"); 365 m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');"); 366 367 QVERIFY(m_page->isModified()); 368 369 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);"); 370 371 QVERIFY(!m_page->isModified()); 372 373 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);"); 374 375 QVERIFY(m_page->isModified()); 376 377 QVERIFY(m_page->history()->canGoBack()); 378 QVERIFY(!m_page->history()->canGoForward()); 379 QCOMPARE(m_page->history()->count(), 2); 380 QVERIFY(m_page->history()->backItem().isValid()); 381 QVERIFY(!m_page->history()->forwardItem().isValid()); 382 383 m_page->history()->back(); 384 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 385 386 QVERIFY(!m_page->history()->canGoBack()); 387 QVERIFY(m_page->history()->canGoForward()); 388 389 QVERIFY(!m_page->isModified()); 390 391 QVERIFY(m_page->history()->currentItemIndex() == 0); 392 393 m_page->history()->setMaximumItemCount(3); 394 QVERIFY(m_page->history()->maximumItemCount() == 3); 395 396 QVariant variant("string test"); 397 m_page->history()->currentItem().setUserData(variant); 398 QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); 399 400 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page")); 401 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page")); 402 QVERIFY(m_page->history()->count() == 2); 403 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page")); 404 QVERIFY(m_page->history()->count() == 2); 405 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page")); 406 QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*)))); 407 } 408 409 void tst_QWebPage::contextMenuCrash() 410 { 411 QWebView view; 412 view.setHtml("<p>test"); 413 view.page()->updatePositionDependentActions(QPoint(0, 0)); 414 QMenu* contextMenu = 0; 415 foreach (QObject* child, view.children()) { 416 contextMenu = qobject_cast<QMenu*>(child); 417 if (contextMenu) 418 break; 419 } 420 QVERIFY(contextMenu); 421 delete contextMenu; 422 } 423 424 void tst_QWebPage::database() 425 { 426 QString path = QDir::currentPath(); 427 m_page->settings()->setOfflineStoragePath(path); 428 QVERIFY(m_page->settings()->offlineStoragePath() == path); 429 430 QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024); 431 QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024); 432 433 m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 434 m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); 435 436 QString dbFileName = path + "Databases.db"; 437 438 if (QFile::exists(dbFileName)) 439 QFile::remove(dbFileName); 440 441 qRegisterMetaType<QWebFrame*>("QWebFrame*"); 442 QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString))); 443 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")); 444 QTRY_COMPARE(spy.count(), 1); 445 m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);"); 446 QTRY_COMPARE(spy.count(),1); 447 448 m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';"); 449 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); 450 451 QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test"); 452 QCOMPARE(s1.toString(), QString("This is a test for local storage")); 453 454 m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';"); 455 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); 456 QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test"); 457 QCOMPARE(s2.toString(), QString("This is a test for session storage")); 458 459 m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com")); 460 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) { });"); 461 QTest::qWait(200); 462 463 // Remove all databases. 464 QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin(); 465 QList<QWebDatabase> dbs = origin.databases(); 466 for (int i = 0; i < dbs.count(); i++) { 467 QString fileName = dbs[i].fileName(); 468 QVERIFY(QFile::exists(fileName)); 469 QWebDatabase::removeDatabase(dbs[i]); 470 QVERIFY(!QFile::exists(fileName)); 471 } 472 QVERIFY(!origin.databases().size()); 473 // Remove removed test :-) 474 QWebDatabase::removeAllDatabases(); 475 QVERIFY(!origin.databases().size()); 476 } 477 478 class PluginPage : public QWebPage 479 { 480 public: 481 PluginPage(QObject *parent = 0) 482 : QWebPage(parent) {} 483 484 struct CallInfo 485 { 486 CallInfo(const QString &c, const QUrl &u, 487 const QStringList &pn, const QStringList &pv, 488 QObject *r) 489 : classid(c), url(u), paramNames(pn), 490 paramValues(pv), returnValue(r) 491 {} 492 QString classid; 493 QUrl url; 494 QStringList paramNames; 495 QStringList paramValues; 496 QObject *returnValue; 497 }; 498 499 QList<CallInfo> calls; 500 501 protected: 502 virtual QObject *createPlugin(const QString &classid, const QUrl &url, 503 const QStringList ¶mNames, 504 const QStringList ¶mValues) 505 { 506 QObject *result = 0; 507 if (classid == "pushbutton") 508 result = new QPushButton(); 509 else if (classid == "lineedit") 510 result = new QLineEdit(); 511 if (result) 512 result->setObjectName(classid); 513 calls.append(CallInfo(classid, url, paramNames, paramValues, result)); 514 return result; 515 } 516 }; 517 518 void tst_QWebPage::createPlugin() 519 { 520 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); 521 522 PluginPage* newPage = new PluginPage(m_view); 523 m_view->setPage(newPage); 524 525 // plugins not enabled by default, so the plugin shouldn't be loaded 526 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>")); 527 QTRY_COMPARE(loadSpy.count(), 1); 528 QCOMPARE(newPage->calls.count(), 0); 529 530 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 531 532 // type has to be application/x-qt-plugin 533 m_view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>")); 534 QTRY_COMPARE(loadSpy.count(), 2); 535 QCOMPARE(newPage->calls.count(), 0); 536 537 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>")); 538 QTRY_COMPARE(loadSpy.count(), 3); 539 QCOMPARE(newPage->calls.count(), 1); 540 { 541 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 542 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); 543 QCOMPARE(ci.url, QUrl()); 544 QCOMPARE(ci.paramNames.count(), 3); 545 QCOMPARE(ci.paramValues.count(), 3); 546 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 547 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 548 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 549 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); 550 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 551 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); 552 QVERIFY(ci.returnValue != 0); 553 QVERIFY(ci.returnValue->inherits("QPushButton")); 554 } 555 // test JS bindings 556 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(), 557 QString::fromLatin1("[object HTMLObjectElement]")); 558 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(), 559 QString::fromLatin1("[object HTMLObjectElement]")); 560 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(), 561 QString::fromLatin1("string")); 562 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(), 563 QString::fromLatin1("pushbutton")); 564 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(), 565 QString::fromLatin1("function")); 566 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(), 567 QString::fromLatin1("function clicked() {\n [native code]\n}")); 568 569 m_view->setHtml(QString("<html><body><table>" 570 "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>" 571 "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>" 572 "</table></body></html>"), QUrl("http://foo.bar.baz")); 573 QTRY_COMPARE(loadSpy.count(), 4); 574 QCOMPARE(newPage->calls.count(), 2); 575 { 576 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 577 QCOMPARE(ci.classid, QString::fromLatin1("lineedit")); 578 QCOMPARE(ci.url, QUrl()); 579 QCOMPARE(ci.paramNames.count(), 3); 580 QCOMPARE(ci.paramValues.count(), 3); 581 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 582 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 583 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 584 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit")); 585 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 586 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit")); 587 QVERIFY(ci.returnValue != 0); 588 QVERIFY(ci.returnValue->inherits("QLineEdit")); 589 } 590 { 591 PluginPage::CallInfo ci = newPage->calls.takeFirst(); 592 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); 593 QCOMPARE(ci.url, QUrl()); 594 QCOMPARE(ci.paramNames.count(), 3); 595 QCOMPARE(ci.paramValues.count(), 3); 596 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); 597 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); 598 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); 599 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); 600 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); 601 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); 602 QVERIFY(ci.returnValue != 0); 603 QVERIFY(ci.returnValue->inherits("QPushButton")); 604 } 605 606 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false); 607 608 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>")); 609 QTRY_COMPARE(loadSpy.count(), 5); 610 QCOMPARE(newPage->calls.count(), 0); 611 } 612 613 614 // Standard base class for template PluginTracerPage. In tests it is used as interface. 615 class PluginCounterPage : public QWebPage { 616 public: 617 int m_count; 618 QPointer<QObject> m_widget; 619 QObject* m_pluginParent; 620 PluginCounterPage(QObject* parent = 0) 621 : QWebPage(parent) 622 , m_count(0) 623 , m_widget(0) 624 , m_pluginParent(0) 625 { 626 settings()->setAttribute(QWebSettings::PluginsEnabled, true); 627 } 628 ~PluginCounterPage() 629 { 630 if (m_pluginParent) 631 m_pluginParent->deleteLater(); 632 } 633 }; 634 635 template<class T> 636 class PluginTracerPage : public PluginCounterPage { 637 public: 638 PluginTracerPage(QObject* parent = 0) 639 : PluginCounterPage(parent) 640 { 641 // this is a dummy parent object for the created plugin 642 m_pluginParent = new T; 643 } 644 virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&) 645 { 646 m_count++; 647 m_widget = new T; 648 // need a cast to the specific type, as QObject::setParent cannot be called, 649 // because it is not virtual. Instead it is necesary to call QWidget::setParent, 650 // which also takes a QWidget* instead of a QObject*. Therefore we need to 651 // upcast to T*, which is a QWidget. 652 static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent)); 653 return m_widget; 654 } 655 }; 656 657 class PluginFactory { 658 public: 659 enum FactoredType {QWidgetType, QGraphicsWidgetType}; 660 static PluginCounterPage* create(FactoredType type, QObject* parent = 0) 661 { 662 PluginCounterPage* result = 0; 663 switch (type) { 664 case QWidgetType: 665 result = new PluginTracerPage<QWidget>(parent); 666 break; 667 case QGraphicsWidgetType: 668 result = new PluginTracerPage<QGraphicsWidget>(parent); 669 break; 670 default: {/*Oops*/}; 671 } 672 return result; 673 } 674 675 static void prepareTestData() 676 { 677 QTest::addColumn<int>("type"); 678 QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType; 679 QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType; 680 } 681 }; 682 683 void tst_QWebPage::destroyPlugin_data() 684 { 685 PluginFactory::prepareTestData(); 686 } 687 688 void tst_QWebPage::destroyPlugin() 689 { 690 QFETCH(int, type); 691 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view); 692 m_view->setPage(page); 693 694 // we create the plugin, so the widget should be constructed 695 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); 696 m_view->setHtml(content); 697 QVERIFY(page->m_widget); 698 QCOMPARE(page->m_count, 1); 699 700 // navigate away, the plugin widget should be destructed 701 m_view->setHtml("<html><body>Hi</body></html>"); 702 QTestEventLoop::instance().enterLoop(1); 703 QVERIFY(!page->m_widget); 704 } 705 706 void tst_QWebPage::createViewlessPlugin_data() 707 { 708 PluginFactory::prepareTestData(); 709 } 710 711 void tst_QWebPage::createViewlessPlugin() 712 { 713 QFETCH(int, type); 714 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type); 715 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); 716 page->mainFrame()->setHtml(content); 717 QCOMPARE(page->m_count, 1); 718 QVERIFY(page->m_widget); 719 QVERIFY(page->m_pluginParent); 720 QVERIFY(page->m_widget->parent() == page->m_pluginParent); 721 delete page; 722 723 } 724 725 // import private API 726 void QWEBKIT_EXPORT qt_webpage_setGroupName(QWebPage* page, const QString& groupName); 727 QString QWEBKIT_EXPORT qt_webpage_groupName(QWebPage* page); 728 729 void tst_QWebPage::multiplePageGroupsAndLocalStorage() 730 { 731 QDir dir(QDir::currentPath()); 732 dir.mkdir("path1"); 733 dir.mkdir("path2"); 734 735 QWebView view1; 736 QWebView view2; 737 738 view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 739 view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1")); 740 qt_webpage_setGroupName(view1.page(), "group1"); 741 view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); 742 view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2")); 743 qt_webpage_setGroupName(view2.page(), "group2"); 744 QCOMPARE(qt_webpage_groupName(view1.page()), QString("group1")); 745 QCOMPARE(qt_webpage_groupName(view2.page()), QString("group2")); 746 747 748 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 749 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 750 751 view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';"); 752 view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';"); 753 754 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 755 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); 756 757 QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test"); 758 QCOMPARE(s1.toString(), QString("value1")); 759 760 QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test"); 761 QCOMPARE(s2.toString(), QString("value2")); 762 763 QTest::qWait(1000); 764 765 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage")); 766 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage")); 767 dir.rmdir(QDir::toNativeSeparators("./path1")); 768 dir.rmdir(QDir::toNativeSeparators("./path2")); 769 } 770 771 class CursorTrackedPage : public QWebPage 772 { 773 public: 774 775 CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) { 776 setViewportSize(QSize(1024, 768)); // big space 777 } 778 779 QString selectedText() { 780 return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString(); 781 } 782 783 int selectionStartOffset() { 784 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt(); 785 } 786 787 int selectionEndOffset() { 788 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt(); 789 } 790 791 // true if start offset == end offset, i.e. no selected text 792 int isSelectionCollapsed() { 793 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool(); 794 } 795 }; 796 797 void tst_QWebPage::cursorMovements() 798 { 799 CursorTrackedPage* page = new CursorTrackedPage; 800 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>"); 801 page->mainFrame()->setHtml(content); 802 803 // this will select the first paragraph 804 QString script = "var range = document.createRange(); " \ 805 "var node = document.getElementById(\"one\"); " \ 806 "range.selectNode(node); " \ 807 "getSelection().addRange(range);"; 808 page->mainFrame()->evaluateJavaScript(script); 809 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); 810 811 // these actions must exist 812 QVERIFY(page->action(QWebPage::MoveToNextChar) != 0); 813 QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0); 814 QVERIFY(page->action(QWebPage::MoveToNextWord) != 0); 815 QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0); 816 QVERIFY(page->action(QWebPage::MoveToNextLine) != 0); 817 QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0); 818 QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0); 819 QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0); 820 QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0); 821 QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0); 822 QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0); 823 QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0); 824 825 // right now they are disabled because contentEditable is false 826 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false); 827 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false); 828 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false); 829 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false); 830 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false); 831 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false); 832 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false); 833 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false); 834 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false); 835 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false); 836 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false); 837 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false); 838 839 // make it editable before navigating the cursor 840 page->setContentEditable(true); 841 842 // here the actions are enabled after contentEditable is true 843 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true); 844 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true); 845 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true); 846 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true); 847 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true); 848 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true); 849 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true); 850 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true); 851 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true); 852 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true); 853 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true); 854 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true); 855 856 // cursor will be before the word "jump" 857 page->triggerAction(QWebPage::MoveToNextChar); 858 QVERIFY(page->isSelectionCollapsed()); 859 QCOMPARE(page->selectionStartOffset(), 0); 860 861 // cursor will be between 'j' and 'u' in the word "jump" 862 page->triggerAction(QWebPage::MoveToNextChar); 863 QVERIFY(page->isSelectionCollapsed()); 864 QCOMPARE(page->selectionStartOffset(), 1); 865 866 // cursor will be between 'u' and 'm' in the word "jump" 867 page->triggerAction(QWebPage::MoveToNextChar); 868 QVERIFY(page->isSelectionCollapsed()); 869 QCOMPARE(page->selectionStartOffset(), 2); 870 871 // cursor will be after the word "jump" 872 page->triggerAction(QWebPage::MoveToNextWord); 873 QVERIFY(page->isSelectionCollapsed()); 874 QCOMPARE(page->selectionStartOffset(), 5); 875 876 // cursor will be after the word "lazy" 877 page->triggerAction(QWebPage::MoveToNextWord); 878 page->triggerAction(QWebPage::MoveToNextWord); 879 page->triggerAction(QWebPage::MoveToNextWord); 880 QVERIFY(page->isSelectionCollapsed()); 881 QCOMPARE(page->selectionStartOffset(), 19); 882 883 // cursor will be between 'z' and 'y' in "lazy" 884 page->triggerAction(QWebPage::MoveToPreviousChar); 885 QVERIFY(page->isSelectionCollapsed()); 886 QCOMPARE(page->selectionStartOffset(), 18); 887 888 // cursor will be between 'a' and 'z' in "lazy" 889 page->triggerAction(QWebPage::MoveToPreviousChar); 890 QVERIFY(page->isSelectionCollapsed()); 891 QCOMPARE(page->selectionStartOffset(), 17); 892 893 // cursor will be before the word "lazy" 894 page->triggerAction(QWebPage::MoveToPreviousWord); 895 QVERIFY(page->isSelectionCollapsed()); 896 QCOMPARE(page->selectionStartOffset(), 15); 897 898 // cursor will be before the word "quick" 899 page->triggerAction(QWebPage::MoveToPreviousWord); 900 page->triggerAction(QWebPage::MoveToPreviousWord); 901 page->triggerAction(QWebPage::MoveToPreviousWord); 902 page->triggerAction(QWebPage::MoveToPreviousWord); 903 page->triggerAction(QWebPage::MoveToPreviousWord); 904 page->triggerAction(QWebPage::MoveToPreviousWord); 905 QVERIFY(page->isSelectionCollapsed()); 906 QCOMPARE(page->selectionStartOffset(), 4); 907 908 // cursor will be between 'p' and 's' in the word "jumps" 909 page->triggerAction(QWebPage::MoveToNextWord); 910 page->triggerAction(QWebPage::MoveToNextWord); 911 page->triggerAction(QWebPage::MoveToNextWord); 912 page->triggerAction(QWebPage::MoveToNextChar); 913 page->triggerAction(QWebPage::MoveToNextChar); 914 page->triggerAction(QWebPage::MoveToNextChar); 915 page->triggerAction(QWebPage::MoveToNextChar); 916 page->triggerAction(QWebPage::MoveToNextChar); 917 QVERIFY(page->isSelectionCollapsed()); 918 QCOMPARE(page->selectionStartOffset(), 4); 919 920 // cursor will be before the word "jumps" 921 page->triggerAction(QWebPage::MoveToStartOfLine); 922 QVERIFY(page->isSelectionCollapsed()); 923 QCOMPARE(page->selectionStartOffset(), 0); 924 925 // cursor will be after the word "dog" 926 page->triggerAction(QWebPage::MoveToEndOfLine); 927 QVERIFY(page->isSelectionCollapsed()); 928 QCOMPARE(page->selectionStartOffset(), 23); 929 930 // cursor will be between 'w' and 'n' in "brown" 931 page->triggerAction(QWebPage::MoveToStartOfLine); 932 page->triggerAction(QWebPage::MoveToPreviousWord); 933 page->triggerAction(QWebPage::MoveToPreviousWord); 934 page->triggerAction(QWebPage::MoveToNextChar); 935 page->triggerAction(QWebPage::MoveToNextChar); 936 page->triggerAction(QWebPage::MoveToNextChar); 937 page->triggerAction(QWebPage::MoveToNextChar); 938 QVERIFY(page->isSelectionCollapsed()); 939 QCOMPARE(page->selectionStartOffset(), 14); 940 941 // cursor will be after the word "fox" 942 page->triggerAction(QWebPage::MoveToEndOfLine); 943 QVERIFY(page->isSelectionCollapsed()); 944 QCOMPARE(page->selectionStartOffset(), 19); 945 946 // cursor will be before the word "The" 947 page->triggerAction(QWebPage::MoveToStartOfDocument); 948 QVERIFY(page->isSelectionCollapsed()); 949 QCOMPARE(page->selectionStartOffset(), 0); 950 951 // cursor will be after the word "you!" 952 page->triggerAction(QWebPage::MoveToEndOfDocument); 953 QVERIFY(page->isSelectionCollapsed()); 954 QCOMPARE(page->selectionStartOffset(), 12); 955 956 // cursor will be before the word "be" 957 page->triggerAction(QWebPage::MoveToStartOfBlock); 958 QVERIFY(page->isSelectionCollapsed()); 959 QCOMPARE(page->selectionStartOffset(), 0); 960 961 // cursor will be after the word "you!" 962 page->triggerAction(QWebPage::MoveToEndOfBlock); 963 QVERIFY(page->isSelectionCollapsed()); 964 QCOMPARE(page->selectionStartOffset(), 12); 965 966 // try to move before the document start 967 page->triggerAction(QWebPage::MoveToStartOfDocument); 968 page->triggerAction(QWebPage::MoveToPreviousChar); 969 QVERIFY(page->isSelectionCollapsed()); 970 QCOMPARE(page->selectionStartOffset(), 0); 971 page->triggerAction(QWebPage::MoveToStartOfDocument); 972 page->triggerAction(QWebPage::MoveToPreviousWord); 973 QVERIFY(page->isSelectionCollapsed()); 974 QCOMPARE(page->selectionStartOffset(), 0); 975 976 // try to move past the document end 977 page->triggerAction(QWebPage::MoveToEndOfDocument); 978 page->triggerAction(QWebPage::MoveToNextChar); 979 QVERIFY(page->isSelectionCollapsed()); 980 QCOMPARE(page->selectionStartOffset(), 12); 981 page->triggerAction(QWebPage::MoveToEndOfDocument); 982 page->triggerAction(QWebPage::MoveToNextWord); 983 QVERIFY(page->isSelectionCollapsed()); 984 QCOMPARE(page->selectionStartOffset(), 12); 985 986 delete page; 987 } 988 989 void tst_QWebPage::textSelection() 990 { 991 CursorTrackedPage* page = new CursorTrackedPage; 992 QString content("<html><body<p id=one>The quick brown fox</p>" \ 993 "<p id=two>jumps over the lazy dog</p>" \ 994 "<p>May the source<br/>be with you!</p></body></html>"); 995 page->mainFrame()->setHtml(content); 996 997 // these actions must exist 998 QVERIFY(page->action(QWebPage::SelectAll) != 0); 999 QVERIFY(page->action(QWebPage::SelectNextChar) != 0); 1000 QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0); 1001 QVERIFY(page->action(QWebPage::SelectNextWord) != 0); 1002 QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0); 1003 QVERIFY(page->action(QWebPage::SelectNextLine) != 0); 1004 QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0); 1005 QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0); 1006 QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0); 1007 QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0); 1008 QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0); 1009 QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0); 1010 QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0); 1011 1012 // right now they are disabled because contentEditable is false and 1013 // there isn't an existing selection to modify 1014 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false); 1015 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false); 1016 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false); 1017 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false); 1018 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false); 1019 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false); 1020 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false); 1021 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false); 1022 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false); 1023 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false); 1024 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false); 1025 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false); 1026 1027 // ..but SelectAll is awalys enabled 1028 QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true); 1029 1030 // this will select the first paragraph 1031 QString selectScript = "var range = document.createRange(); " \ 1032 "var node = document.getElementById(\"one\"); " \ 1033 "range.selectNode(node); " \ 1034 "getSelection().addRange(range);"; 1035 page->mainFrame()->evaluateJavaScript(selectScript); 1036 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); 1037 1038 // here the actions are enabled after a selection has been created 1039 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); 1040 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); 1041 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); 1042 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); 1043 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); 1044 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); 1045 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); 1046 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); 1047 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); 1048 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); 1049 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); 1050 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); 1051 1052 // make it editable before navigating the cursor 1053 page->setContentEditable(true); 1054 1055 // cursor will be before the word "The", this makes sure there is a charet 1056 page->triggerAction(QWebPage::MoveToStartOfDocument); 1057 QVERIFY(page->isSelectionCollapsed()); 1058 QCOMPARE(page->selectionStartOffset(), 0); 1059 1060 // here the actions are enabled after contentEditable is true 1061 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); 1062 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); 1063 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); 1064 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); 1065 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); 1066 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); 1067 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); 1068 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); 1069 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); 1070 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); 1071 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); 1072 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); 1073 1074 delete page; 1075 } 1076 1077 void tst_QWebPage::textEditing() 1078 { 1079 CursorTrackedPage* page = new CursorTrackedPage; 1080 QString content("<html><body<p id=one>The quick brown fox</p>" \ 1081 "<p id=two>jumps over the lazy dog</p>" \ 1082 "<p>May the source<br/>be with you!</p></body></html>"); 1083 page->mainFrame()->setHtml(content); 1084 1085 // these actions must exist 1086 QVERIFY(page->action(QWebPage::Cut) != 0); 1087 QVERIFY(page->action(QWebPage::Copy) != 0); 1088 QVERIFY(page->action(QWebPage::Paste) != 0); 1089 QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0); 1090 QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0); 1091 QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0); 1092 QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0); 1093 QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0); 1094 QVERIFY(page->action(QWebPage::ToggleBold) != 0); 1095 QVERIFY(page->action(QWebPage::ToggleItalic) != 0); 1096 QVERIFY(page->action(QWebPage::ToggleUnderline) != 0); 1097 QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0); 1098 QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0); 1099 QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0); 1100 QVERIFY(page->action(QWebPage::RemoveFormat) != 0); 1101 QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0); 1102 QVERIFY(page->action(QWebPage::ToggleSubscript) != 0); 1103 QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0); 1104 QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0); 1105 QVERIFY(page->action(QWebPage::InsertOrderedList) != 0); 1106 QVERIFY(page->action(QWebPage::Indent) != 0); 1107 QVERIFY(page->action(QWebPage::Outdent) != 0); 1108 QVERIFY(page->action(QWebPage::AlignCenter) != 0); 1109 QVERIFY(page->action(QWebPage::AlignJustified) != 0); 1110 QVERIFY(page->action(QWebPage::AlignLeft) != 0); 1111 QVERIFY(page->action(QWebPage::AlignRight) != 0); 1112 1113 // right now they are disabled because contentEditable is false 1114 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); 1115 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false); 1116 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false); 1117 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false); 1118 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false); 1119 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false); 1120 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false); 1121 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false); 1122 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false); 1123 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false); 1124 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false); 1125 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false); 1126 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false); 1127 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); 1128 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false); 1129 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false); 1130 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false); 1131 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false); 1132 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false); 1133 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false); 1134 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false); 1135 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false); 1136 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false); 1137 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false); 1138 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false); 1139 1140 // Select everything 1141 page->triggerAction(QWebPage::SelectAll); 1142 1143 // make sure it is enabled since there is a selection 1144 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true); 1145 1146 // make it editable before navigating the cursor 1147 page->setContentEditable(true); 1148 1149 // clear the selection 1150 page->triggerAction(QWebPage::MoveToStartOfDocument); 1151 QVERIFY(page->isSelectionCollapsed()); 1152 QCOMPARE(page->selectionStartOffset(), 0); 1153 1154 // make sure it is disabled since there isn't a selection 1155 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false); 1156 1157 // here the actions are enabled after contentEditable is true 1158 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true); 1159 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true); 1160 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true); 1161 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true); 1162 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true); 1163 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true); 1164 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true); 1165 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true); 1166 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true); 1167 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true); 1168 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true); 1169 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true); 1170 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true); 1171 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true); 1172 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true); 1173 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true); 1174 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true); 1175 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true); 1176 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true); 1177 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true); 1178 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true); 1179 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true); 1180 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true); 1181 1182 // make sure these are disabled since there isn't a selection 1183 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); 1184 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); 1185 1186 // make sure everything is selected 1187 page->triggerAction(QWebPage::SelectAll); 1188 1189 // this is only true if there is an editable selection 1190 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true); 1191 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true); 1192 1193 delete page; 1194 } 1195 1196 void tst_QWebPage::requestCache() 1197 { 1198 TestPage page; 1199 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); 1200 1201 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); 1202 QTRY_COMPARE(loadSpy.count(), 1); 1203 QTRY_COMPARE(page.navigations.count(), 1); 1204 1205 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>")); 1206 QTRY_COMPARE(loadSpy.count(), 2); 1207 QTRY_COMPARE(page.navigations.count(), 2); 1208 1209 page.triggerAction(QWebPage::Stop); 1210 QVERIFY(page.history()->canGoBack()); 1211 page.triggerAction(QWebPage::Back); 1212 1213 QTRY_COMPARE(loadSpy.count(), 3); 1214 QTRY_COMPARE(page.navigations.count(), 3); 1215 QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1216 (int)QNetworkRequest::PreferNetwork); 1217 QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1218 (int)QNetworkRequest::PreferNetwork); 1219 QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), 1220 (int)QNetworkRequest::PreferCache); 1221 } 1222 1223 void tst_QWebPage::backActionUpdate() 1224 { 1225 QWebView view; 1226 QWebPage *page = view.page(); 1227 QAction *action = page->action(QWebPage::Back); 1228 QVERIFY(!action->isEnabled()); 1229 QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool))); 1230 QUrl url = QUrl("qrc:///resources/index.html"); 1231 page->mainFrame()->load(url); 1232 QTRY_COMPARE(loadSpy.count(), 1); 1233 QVERIFY(!action->isEnabled()); 1234 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10)); 1235 QTRY_COMPARE(loadSpy.count(), 2); 1236 1237 QVERIFY(action->isEnabled()); 1238 } 1239 1240 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition) 1241 { 1242 if (!webFrame) 1243 return; 1244 1245 framePosition += QPoint(webFrame->pos()); 1246 QList<QWebFrame*> children = webFrame->childFrames(); 1247 for (int i = 0; i < children.size(); ++i) { 1248 if (children.at(i)->childFrames().size() > 0) 1249 frameAtHelper(webPage, children.at(i), framePosition); 1250 1251 QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size()); 1252 QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft())); 1253 } 1254 } 1255 1256 void tst_QWebPage::frameAt() 1257 { 1258 QWebView webView; 1259 QWebPage* webPage = webView.page(); 1260 QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); 1261 QUrl url = QUrl("qrc:///resources/iframe.html"); 1262 webPage->mainFrame()->load(url); 1263 QTRY_COMPARE(loadSpy.count(), 1); 1264 frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos()); 1265 } 1266 1267 void tst_QWebPage::inputMethods_data() 1268 { 1269 QTest::addColumn<QString>("viewType"); 1270 QTest::newRow("QWebView") << "QWebView"; 1271 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1272 QTest::newRow("QGraphicsWebView") << "QGraphicsWebView"; 1273 #endif 1274 } 1275 1276 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1277 static Qt::InputMethodHints inputMethodHints(QObject* object) 1278 { 1279 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) 1280 return o->inputMethodHints(); 1281 if (QWidget* w = qobject_cast<QWidget*>(object)) 1282 return w->inputMethodHints(); 1283 return Qt::InputMethodHints(); 1284 } 1285 #endif 1286 1287 static bool inputMethodEnabled(QObject* object) 1288 { 1289 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1290 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) 1291 return o->flags() & QGraphicsItem::ItemAcceptsInputMethod; 1292 #endif 1293 if (QWidget* w = qobject_cast<QWidget*>(object)) 1294 return w->testAttribute(Qt::WA_InputMethodEnabled); 1295 return false; 1296 } 1297 1298 void tst_QWebPage::inputMethods() 1299 { 1300 QFETCH(QString, viewType); 1301 QWebPage* page = new QWebPage; 1302 QObject* view = 0; 1303 QObject* container = 0; 1304 if (viewType == "QWebView") { 1305 QWebView* wv = new QWebView; 1306 wv->setPage(page); 1307 view = wv; 1308 container = view; 1309 } 1310 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1311 else if (viewType == "QGraphicsWebView") { 1312 QGraphicsWebView* wv = new QGraphicsWebView; 1313 wv->setPage(page); 1314 view = wv; 1315 1316 QGraphicsView* gv = new QGraphicsView; 1317 QGraphicsScene* scene = new QGraphicsScene(gv); 1318 gv->setScene(scene); 1319 scene->addItem(wv); 1320 wv->setGeometry(QRect(0, 0, 500, 500)); 1321 1322 container = gv; 1323 } 1324 #endif 1325 else 1326 QVERIFY2(false, "Unknown view type"); 1327 1328 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont"); 1329 page->mainFrame()->setHtml("<html><body>" \ 1330 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \ 1331 "<input type='password'/>" \ 1332 "</body></html>"); 1333 page->mainFrame()->setFocus(); 1334 1335 EventSpy viewEventSpy(container); 1336 1337 QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input"); 1338 1339 QMouseEvent evpres(QEvent::MouseButtonPress, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1340 page->event(&evpres); 1341 QMouseEvent evrel(QEvent::MouseButtonRelease, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1342 page->event(&evrel); 1343 1344 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1345 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1346 #endif 1347 viewEventSpy.clear(); 1348 1349 page->event(&evpres); 1350 page->event(&evrel); 1351 1352 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1353 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1354 #endif 1355 1356 //ImMicroFocus 1357 QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus); 1358 QRect focusRect = variant.toRect(); 1359 QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft())); 1360 1361 //ImFont 1362 variant = page->inputMethodQuery(Qt::ImFont); 1363 QFont font = variant.value<QFont>(); 1364 QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family()); 1365 1366 QList<QInputMethodEvent::Attribute> inputAttributes; 1367 1368 //Insert text. 1369 { 1370 QInputMethodEvent eventText("QtWebKit", inputAttributes); 1371 QSignalSpy signalSpy(page, SIGNAL(microFocusChanged())); 1372 page->event(&eventText); 1373 QCOMPARE(signalSpy.count(), 0); 1374 } 1375 1376 { 1377 QInputMethodEvent eventText("", inputAttributes); 1378 eventText.setCommitString(QString("QtWebKit"), 0, 0); 1379 page->event(&eventText); 1380 } 1381 1382 #if QT_VERSION >= 0x040600 1383 //ImMaximumTextLength 1384 variant = page->inputMethodQuery(Qt::ImMaximumTextLength); 1385 QCOMPARE(20, variant.toInt()); 1386 1387 //Set selection 1388 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant()); 1389 QInputMethodEvent eventSelection("",inputAttributes); 1390 page->event(&eventSelection); 1391 1392 //ImAnchorPosition 1393 variant = page->inputMethodQuery(Qt::ImAnchorPosition); 1394 int anchorPosition = variant.toInt(); 1395 QCOMPARE(anchorPosition, 3); 1396 1397 //ImCursorPosition 1398 variant = page->inputMethodQuery(Qt::ImCursorPosition); 1399 int cursorPosition = variant.toInt(); 1400 QCOMPARE(cursorPosition, 5); 1401 1402 //ImCurrentSelection 1403 variant = page->inputMethodQuery(Qt::ImCurrentSelection); 1404 QString selectionValue = variant.value<QString>(); 1405 QCOMPARE(selectionValue, QString("eb")); 1406 #endif 1407 1408 //ImSurroundingText 1409 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1410 QString value = variant.value<QString>(); 1411 QCOMPARE(value, QString("QtWebKit")); 1412 1413 #if QT_VERSION >= 0x040600 1414 { 1415 QList<QInputMethodEvent::Attribute> attributes; 1416 // Clear the selection, so the next test does not clear any contents. 1417 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); 1418 attributes.append(newSelection); 1419 QInputMethodEvent event("composition", attributes); 1420 page->event(&event); 1421 } 1422 1423 // A ongoing composition should not change the surrounding text before it is committed. 1424 variant = page->inputMethodQuery(Qt::ImSurroundingText); 1425 value = variant.value<QString>(); 1426 QCOMPARE(value, QString("QtWebKit")); 1427 #endif 1428 1429 //ImhHiddenText 1430 QMouseEvent evpresPassword(QEvent::MouseButtonPress, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1431 page->event(&evpresPassword); 1432 QMouseEvent evrelPassword(QEvent::MouseButtonRelease, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1433 page->event(&evrelPassword); 1434 1435 QVERIFY(inputMethodEnabled(view)); 1436 #if QT_VERSION >= 0x040600 1437 QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText); 1438 1439 page->event(&evpres); 1440 page->event(&evrel); 1441 QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText)); 1442 #endif 1443 1444 page->mainFrame()->setHtml("<html><body><p>nothing to input here"); 1445 viewEventSpy.clear(); 1446 1447 QWebElement para = page->mainFrame()->findFirstElement("p"); 1448 { 1449 QMouseEvent evpres(QEvent::MouseButtonPress, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1450 page->event(&evpres); 1451 QMouseEvent evrel(QEvent::MouseButtonRelease, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 1452 page->event(&evrel); 1453 } 1454 1455 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1456 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); 1457 #endif 1458 1459 delete container; 1460 } 1461 1462 // import a little DRT helper function to trigger the garbage collector 1463 void QWEBKIT_EXPORT qt_drt_garbageCollector_collect(); 1464 1465 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector() 1466 { 1467 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); 1468 1469 PluginPage* newPage = new PluginPage(m_view); 1470 m_view->setPage(newPage); 1471 1472 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 1473 1474 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>")); 1475 QTRY_COMPARE(loadSpy.count(), 1); 1476 1477 newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }"); 1478 1479 newPage->mainFrame()->evaluateJavaScript("testme('foo')"); 1480 1481 qt_drt_garbageCollector_collect(); 1482 1483 // don't crash! 1484 newPage->mainFrame()->evaluateJavaScript("testme('bar')"); 1485 } 1486 1487 void tst_QWebPage::localURLSchemes() 1488 { 1489 int i = QWebSecurityOrigin::localSchemes().size(); 1490 1491 QWebSecurityOrigin::removeLocalScheme("file"); 1492 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 1493 QWebSecurityOrigin::addLocalScheme("file"); 1494 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 1495 1496 QWebSecurityOrigin::removeLocalScheme("qrc"); 1497 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1); 1498 QWebSecurityOrigin::addLocalScheme("qrc"); 1499 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 1500 1501 QString myscheme = "myscheme"; 1502 QWebSecurityOrigin::addLocalScheme(myscheme); 1503 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1); 1504 QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme)); 1505 QWebSecurityOrigin::removeLocalScheme(myscheme); 1506 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 1507 QWebSecurityOrigin::removeLocalScheme(myscheme); 1508 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); 1509 } 1510 1511 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue) 1512 { 1513 webPage.settings()->setAttribute(settingAttribute, settingValue); 1514 return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool(); 1515 } 1516 1517 void tst_QWebPage::testOptionalJSObjects() 1518 { 1519 // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off 1520 // the visibility of the JS object any more. For this reason this test uses two QWebPage instances. 1521 // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on 1522 // a feature for one instance will not turn it on for another. 1523 1524 QWebPage webPage1; 1525 QWebPage webPage2; 1526 1527 webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); 1528 webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); 1529 1530 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); 1531 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); 1532 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true); 1533 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); 1534 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); 1535 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true); 1536 1537 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); 1538 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true); 1539 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); 1540 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true); 1541 } 1542 1543 void tst_QWebPage::testEnablePersistentStorage() 1544 { 1545 QWebPage webPage; 1546 1547 // By default all persistent options should be disabled 1548 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false); 1549 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false); 1550 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false); 1551 QVERIFY(webPage.settings()->iconDatabasePath().isEmpty()); 1552 1553 QWebSettings::enablePersistentStorage(); 1554 1555 1556 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true); 1557 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true); 1558 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true); 1559 1560 QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty()); 1561 QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty()); 1562 QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty()); 1563 } 1564 1565 void tst_QWebPage::defaultTextEncoding() 1566 { 1567 QWebFrame* mainFrame = m_page->mainFrame(); 1568 1569 QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 1570 QVERIFY(!defaultCharset.isEmpty()); 1571 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset); 1572 1573 m_page->settings()->setDefaultTextEncoding(QString("utf-8")); 1574 QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 1575 QCOMPARE(charset, QString("utf-8")); 1576 QCOMPARE(m_page->settings()->defaultTextEncoding(), charset); 1577 1578 m_page->settings()->setDefaultTextEncoding(QString()); 1579 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 1580 QVERIFY(!charset.isEmpty()); 1581 QCOMPARE(charset, defaultCharset); 1582 1583 QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8")); 1584 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); 1585 QCOMPARE(charset, QString("utf-8")); 1586 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset); 1587 } 1588 1589 class ErrorPage : public QWebPage 1590 { 1591 public: 1592 1593 ErrorPage(QWidget* parent = 0): QWebPage(parent) 1594 { 1595 } 1596 1597 virtual bool supportsExtension(Extension extension) const 1598 { 1599 return extension == ErrorPageExtension; 1600 } 1601 1602 virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output) 1603 { 1604 ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output); 1605 1606 errorPage->content = "data:text/html,error"; 1607 return true; 1608 } 1609 }; 1610 1611 void tst_QWebPage::errorPageExtension() 1612 { 1613 ErrorPage* page = new ErrorPage; 1614 m_view->setPage(page); 1615 1616 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 1617 1618 m_view->setUrl(QUrl("data:text/html,foo")); 1619 QTRY_COMPARE(spyLoadFinished.count(), 1); 1620 1621 page->mainFrame()->setUrl(QUrl("http://non.existent/url")); 1622 QTRY_COMPARE(spyLoadFinished.count(), 2); 1623 QCOMPARE(page->mainFrame()->toPlainText(), QString("data:text/html,error")); 1624 QCOMPARE(page->history()->count(), 2); 1625 QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url")); 1626 QCOMPARE(page->history()->canGoBack(), true); 1627 QCOMPARE(page->history()->canGoForward(), false); 1628 1629 page->triggerAction(QWebPage::Back); 1630 QTRY_COMPARE(page->history()->canGoBack(), false); 1631 QTRY_COMPARE(page->history()->canGoForward(), true); 1632 1633 page->triggerAction(QWebPage::Forward); 1634 QTRY_COMPARE(page->history()->canGoBack(), true); 1635 QTRY_COMPARE(page->history()->canGoForward(), false); 1636 1637 page->triggerAction(QWebPage::Back); 1638 QTRY_COMPARE(page->history()->canGoBack(), false); 1639 QTRY_COMPARE(page->history()->canGoForward(), true); 1640 QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo")); 1641 1642 m_view->setPage(0); 1643 } 1644 1645 void tst_QWebPage::errorPageExtensionInIFrames() 1646 { 1647 ErrorPage* page = new ErrorPage; 1648 m_view->setPage(page); 1649 1650 m_view->setHtml(QString("data:text/html," 1651 "<h1>h1</h1>" 1652 "<iframe src='data:text/html,<p/>p'></iframe>" 1653 "<iframe src='non-existent.html'></iframe>")); 1654 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 1655 QTRY_COMPARE(spyLoadFinished.count(), 1); 1656 1657 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error")); 1658 1659 m_view->setPage(0); 1660 } 1661 1662 void tst_QWebPage::errorPageExtensionInFrameset() 1663 { 1664 ErrorPage* page = new ErrorPage; 1665 m_view->setPage(page); 1666 1667 m_view->load(QUrl("qrc:///resources/index.html")); 1668 1669 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); 1670 QTRY_COMPARE(spyLoadFinished.count(), 1); 1671 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error")); 1672 1673 m_view->setPage(0); 1674 } 1675 1676 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame() 1677 { 1678 { 1679 QWebPage webPage; 1680 } 1681 { 1682 QWebPage webPage; 1683 webPage.selectedText(); 1684 } 1685 { 1686 QWebPage webPage; 1687 webPage.triggerAction(QWebPage::Back, true); 1688 } 1689 { 1690 QWebPage webPage; 1691 QPoint pos(10,10); 1692 webPage.updatePositionDependentActions(pos); 1693 } 1694 } 1695 1696 static void takeScreenshot(QWebPage* page) 1697 { 1698 QWebFrame* mainFrame = page->mainFrame(); 1699 page->setViewportSize(mainFrame->contentsSize()); 1700 QImage image(page->viewportSize(), QImage::Format_ARGB32); 1701 QPainter painter(&image); 1702 mainFrame->render(&painter); 1703 painter.end(); 1704 } 1705 1706 void tst_QWebPage::screenshot_data() 1707 { 1708 QTest::addColumn<QString>("html"); 1709 QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; 1710 QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); 1711 QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>"); 1712 } 1713 1714 void tst_QWebPage::screenshot() 1715 { 1716 if (!QDir(TESTS_SOURCE_DIR).exists()) 1717 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); 1718 1719 QDir::setCurrent(TESTS_SOURCE_DIR); 1720 1721 QFETCH(QString, html); 1722 QWebPage* page = new QWebPage; 1723 page->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 1724 QWebFrame* mainFrame = page->mainFrame(); 1725 mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); 1726 ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000); 1727 1728 // take screenshot without a view 1729 takeScreenshot(page); 1730 1731 QWebView* view = new QWebView; 1732 view->setPage(page); 1733 1734 // take screenshot when attached to a view 1735 takeScreenshot(page); 1736 1737 delete page; 1738 delete view; 1739 1740 QDir::setCurrent(QApplication::applicationDirPath()); 1741 } 1742 1743 void tst_QWebPage::originatingObjectInNetworkRequests() 1744 { 1745 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 1746 m_page->setNetworkAccessManager(networkManager); 1747 networkManager->requests.clear(); 1748 1749 m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," 1750 "<head><meta http-equiv='refresh' content='1'></head>foo \">" 1751 "<frame src=\"data:text/html,bar\"></frameset>"), QUrl()); 1752 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); 1753 1754 QCOMPARE(networkManager->requests.count(), 2); 1755 1756 QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames(); 1757 QCOMPARE(childFrames.count(), 2); 1758 1759 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 1760 for (int i = 0; i < 2; ++i) 1761 QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i)); 1762 #endif 1763 } 1764 1765 /** 1766 * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914 1767 * 1768 * From JS we test the following conditions. 1769 * 1770 * OK + QString() => SUCCESS, empty string (but not null) 1771 * OK + "text" => SUCCESS, "text" 1772 * CANCEL + QString() => CANCEL, null string 1773 * CANCEL + "text" => CANCEL, null string 1774 */ 1775 class JSPromptPage : public QWebPage { 1776 Q_OBJECT 1777 public: 1778 JSPromptPage() 1779 {} 1780 1781 bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result) 1782 { 1783 if (msg == QLatin1String("test1")) { 1784 *result = QString(); 1785 return true; 1786 } else if (msg == QLatin1String("test2")) { 1787 *result = QLatin1String("text"); 1788 return true; 1789 } else if (msg == QLatin1String("test3")) { 1790 *result = QString(); 1791 return false; 1792 } else if (msg == QLatin1String("test4")) { 1793 *result = QLatin1String("text"); 1794 return false; 1795 } 1796 1797 qFatal("Unknown msg."); 1798 return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result); 1799 } 1800 }; 1801 1802 void tst_QWebPage::testJSPrompt() 1803 { 1804 JSPromptPage page; 1805 bool res; 1806 1807 // OK + QString() 1808 res = page.mainFrame()->evaluateJavaScript( 1809 "var retval = prompt('test1');" 1810 "retval=='' && retval.length == 0;").toBool(); 1811 QVERIFY(res); 1812 1813 // OK + "text" 1814 res = page.mainFrame()->evaluateJavaScript( 1815 "var retval = prompt('test2');" 1816 "retval=='text' && retval.length == 4;").toBool(); 1817 QVERIFY(res); 1818 1819 // Cancel + QString() 1820 res = page.mainFrame()->evaluateJavaScript( 1821 "var retval = prompt('test3');" 1822 "retval===null;").toBool(); 1823 QVERIFY(res); 1824 1825 // Cancel + "text" 1826 res = page.mainFrame()->evaluateJavaScript( 1827 "var retval = prompt('test4');" 1828 "retval===null;").toBool(); 1829 QVERIFY(res); 1830 } 1831 1832 class TestModalPage : public QWebPage 1833 { 1834 Q_OBJECT 1835 public: 1836 TestModalPage(QObject* parent = 0) : QWebPage(parent) { 1837 } 1838 virtual QWebPage* createWindow(WebWindowType) { 1839 QWebPage* page = new TestModalPage(); 1840 connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater())); 1841 return page; 1842 } 1843 }; 1844 1845 void tst_QWebPage::showModalDialog() 1846 { 1847 TestModalPage page; 1848 page.mainFrame()->setHtml(QString("<html></html>")); 1849 QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString(); 1850 QCOMPARE(res, QString("This is a test")); 1851 } 1852 1853 QTEST_MAIN(tst_QWebPage) 1854 #include "tst_qwebpage.moc" 1855