1 /* 2 Copyright (C) 2008,2009 Nokia Corporation and/or its subsidiary(-ies) 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18 */ 19 20 21 #include <QtTest/QtTest> 22 23 #include <qwebpage.h> 24 #include <qwebelement.h> 25 #include <qwidget.h> 26 #include <qwebview.h> 27 #include <qwebframe.h> 28 #include <qwebhistory.h> 29 #include <QAbstractItemView> 30 #include <QApplication> 31 #include <QComboBox> 32 #include <QPaintEngine> 33 #include <QPicture> 34 #include <QRegExp> 35 #include <QNetworkRequest> 36 #include <QNetworkReply> 37 #include <QTextCodec> 38 #ifndef QT_NO_OPENSSL 39 #include <qsslerror.h> 40 #endif 41 #include "../util.h" 42 43 struct CustomType { 44 QString string; 45 }; 46 Q_DECLARE_METATYPE(CustomType) 47 48 Q_DECLARE_METATYPE(QBrush*) 49 Q_DECLARE_METATYPE(QObjectList) 50 Q_DECLARE_METATYPE(QList<int>) 51 Q_DECLARE_METATYPE(Qt::BrushStyle) 52 Q_DECLARE_METATYPE(QVariantList) 53 Q_DECLARE_METATYPE(QVariantMap) 54 55 class MyQObject : public QObject 56 { 57 Q_OBJECT 58 59 Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty) 60 Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty) 61 Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty) 62 Q_PROPERTY(QVariantMap variantMapProperty READ variantMapProperty WRITE setVariantMapProperty) 63 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) 64 Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty) 65 Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty) 66 Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty) 67 Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false) 68 Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty) 69 Q_PROPERTY(int readOnlyProperty READ readOnlyProperty) 70 Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) 71 Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType) 72 Q_PROPERTY(QWebElement webElementProperty READ webElementProperty WRITE setWebElementProperty) 73 Q_PROPERTY(QObject* objectStarProperty READ objectStarProperty WRITE setObjectStarProperty) 74 Q_ENUMS(Policy Strategy) 75 Q_FLAGS(Ability) 76 77 public: 78 enum Policy { 79 FooPolicy = 0, 80 BarPolicy, 81 BazPolicy 82 }; 83 84 enum Strategy { 85 FooStrategy = 10, 86 BarStrategy, 87 BazStrategy 88 }; 89 90 enum AbilityFlag { 91 NoAbility = 0x000, 92 FooAbility = 0x001, 93 BarAbility = 0x080, 94 BazAbility = 0x200, 95 AllAbility = FooAbility | BarAbility | BazAbility 96 }; 97 98 Q_DECLARE_FLAGS(Ability, AbilityFlag) 99 100 MyQObject(QObject* parent = 0) 101 : QObject(parent), 102 m_intValue(123), 103 m_variantValue(QLatin1String("foo")), 104 m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))), 105 m_stringValue(QLatin1String("bar")), 106 m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")), 107 m_brushValue(QColor(10, 20, 30, 40)), 108 m_hiddenValue(456.0), 109 m_writeOnlyValue(789), 110 m_readOnlyValue(987), 111 m_objectStar(0), 112 m_qtFunctionInvoked(-1) 113 { 114 m_variantMapValue.insert("a", QVariant(123)); 115 m_variantMapValue.insert("b", QVariant(QLatin1String("foo"))); 116 m_variantMapValue.insert("c", QVariant::fromValue<QObject*>(this)); 117 } 118 119 ~MyQObject() { } 120 121 int intProperty() const { 122 return m_intValue; 123 } 124 void setIntProperty(int value) { 125 m_intValue = value; 126 } 127 128 QVariant variantProperty() const { 129 return m_variantValue; 130 } 131 void setVariantProperty(const QVariant &value) { 132 m_variantValue = value; 133 } 134 135 QVariantList variantListProperty() const { 136 return m_variantListValue; 137 } 138 void setVariantListProperty(const QVariantList &value) { 139 m_variantListValue = value; 140 } 141 142 QVariantMap variantMapProperty() const { 143 return m_variantMapValue; 144 } 145 void setVariantMapProperty(const QVariantMap &value) { 146 m_variantMapValue = value; 147 } 148 149 QString stringProperty() const { 150 return m_stringValue; 151 } 152 void setStringProperty(const QString &value) { 153 m_stringValue = value; 154 } 155 156 QStringList stringListProperty() const { 157 return m_stringListValue; 158 } 159 void setStringListProperty(const QStringList &value) { 160 m_stringListValue = value; 161 } 162 163 QByteArray byteArrayProperty() const { 164 return m_byteArrayValue; 165 } 166 void setByteArrayProperty(const QByteArray &value) { 167 m_byteArrayValue = value; 168 } 169 170 QBrush brushProperty() const { 171 return m_brushValue; 172 } 173 Q_INVOKABLE void setBrushProperty(const QBrush &value) { 174 m_brushValue = value; 175 } 176 177 double hiddenProperty() const { 178 return m_hiddenValue; 179 } 180 void setHiddenProperty(double value) { 181 m_hiddenValue = value; 182 } 183 184 int writeOnlyProperty() const { 185 return m_writeOnlyValue; 186 } 187 void setWriteOnlyProperty(int value) { 188 m_writeOnlyValue = value; 189 } 190 191 int readOnlyProperty() const { 192 return m_readOnlyValue; 193 } 194 195 QKeySequence shortcut() const { 196 return m_shortcut; 197 } 198 void setShortcut(const QKeySequence &seq) { 199 m_shortcut = seq; 200 } 201 202 QWebElement webElementProperty() const { 203 return m_webElement; 204 } 205 206 void setWebElementProperty(const QWebElement& element) { 207 m_webElement = element; 208 } 209 210 CustomType propWithCustomType() const { 211 return m_customType; 212 } 213 void setPropWithCustomType(const CustomType &c) { 214 m_customType = c; 215 } 216 217 QObject* objectStarProperty() const { 218 return m_objectStar; 219 } 220 221 void setObjectStarProperty(QObject* object) { 222 m_objectStar = object; 223 } 224 225 226 int qtFunctionInvoked() const { 227 return m_qtFunctionInvoked; 228 } 229 230 QVariantList qtFunctionActuals() const { 231 return m_actuals; 232 } 233 234 void resetQtFunctionInvoked() { 235 m_qtFunctionInvoked = -1; 236 m_actuals.clear(); 237 } 238 239 Q_INVOKABLE void myInvokable() { 240 m_qtFunctionInvoked = 0; 241 } 242 Q_INVOKABLE void myInvokableWithIntArg(int arg) { 243 m_qtFunctionInvoked = 1; 244 m_actuals << arg; 245 } 246 Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) { 247 m_qtFunctionInvoked = 2; 248 m_actuals << arg; 249 } 250 Q_INVOKABLE void myInvokableWithFloatArg(float arg) { 251 m_qtFunctionInvoked = 3; 252 m_actuals << arg; 253 } 254 Q_INVOKABLE void myInvokableWithDoubleArg(double arg) { 255 m_qtFunctionInvoked = 4; 256 m_actuals << arg; 257 } 258 Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) { 259 m_qtFunctionInvoked = 5; 260 m_actuals << arg; 261 } 262 Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) { 263 m_qtFunctionInvoked = 6; 264 m_actuals << arg1 << arg2; 265 } 266 Q_INVOKABLE int myInvokableReturningInt() { 267 m_qtFunctionInvoked = 7; 268 return 123; 269 } 270 Q_INVOKABLE qlonglong myInvokableReturningLongLong() { 271 m_qtFunctionInvoked = 39; 272 return 456; 273 } 274 Q_INVOKABLE QString myInvokableReturningString() { 275 m_qtFunctionInvoked = 8; 276 return QLatin1String("ciao"); 277 } 278 Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload 279 m_qtFunctionInvoked = 9; 280 m_actuals << arg1 << arg2; 281 } 282 Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) { 283 m_qtFunctionInvoked = 10; 284 m_actuals << policy; 285 } 286 Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) { 287 m_qtFunctionInvoked = 36; 288 m_actuals << policy; 289 } 290 Q_INVOKABLE Policy myInvokableReturningEnum() { 291 m_qtFunctionInvoked = 37; 292 return BazPolicy; 293 } 294 Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() { 295 m_qtFunctionInvoked = 38; 296 return BazPolicy; 297 } 298 Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() { 299 m_qtFunctionInvoked = 11; 300 return QVector<int>(); 301 } 302 Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) { 303 m_qtFunctionInvoked = 12; 304 } 305 Q_INVOKABLE QObject* myInvokableReturningQObjectStar() { 306 m_qtFunctionInvoked = 13; 307 return this; 308 } 309 Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) { 310 m_qtFunctionInvoked = 14; 311 m_actuals << QVariant::fromValue(lst); 312 return lst; 313 } 314 Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) { 315 m_qtFunctionInvoked = 15; 316 m_actuals << v; 317 return v; 318 } 319 Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) { 320 m_qtFunctionInvoked = 16; 321 m_actuals << vm; 322 return vm; 323 } 324 Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) { 325 m_qtFunctionInvoked = 17; 326 m_actuals << QVariant::fromValue(lst); 327 return lst; 328 } 329 Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) { 330 m_qtFunctionInvoked = 18; 331 m_actuals << QVariant::fromValue(obj); 332 return obj; 333 } 334 Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) { 335 m_qtFunctionInvoked = 19; 336 m_actuals << QVariant::fromValue(brush); 337 return brush; 338 } 339 Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) { 340 m_qtFunctionInvoked = 43; 341 m_actuals << QVariant::fromValue(style); 342 } 343 Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) { 344 m_qtFunctionInvoked = 44; 345 m_actuals << QVariant::fromValue(arg); 346 } 347 Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) { 348 m_qtFunctionInvoked = 45; 349 m_actuals << QVariant::fromValue(arg); 350 } 351 Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) { 352 m_qtFunctionInvoked = 46; 353 m_actuals << QVariant::fromValue(arg); 354 } 355 Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") { 356 m_qtFunctionInvoked = 47; 357 m_actuals << QVariant::fromValue(arg1) << qVariantFromValue(arg2); 358 } 359 Q_INVOKABLE QObject& myInvokableReturningRef() { 360 m_qtFunctionInvoked = 48; 361 return *this; 362 } 363 Q_INVOKABLE const QObject& myInvokableReturningConstRef() const { 364 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49; 365 return *this; 366 } 367 Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) { 368 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50; 369 m_actuals << QVariant::fromValue(arg); 370 } 371 Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) { 372 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51; 373 m_actuals << QVariant::fromValue(arg); 374 } 375 Q_INVOKABLE void myInvokableWithBoolArg(bool arg) { 376 m_qtFunctionInvoked = 52; 377 m_actuals << arg; 378 } 379 380 void emitMySignal() { 381 emit mySignal(); 382 } 383 void emitMySignalWithIntArg(int arg) { 384 emit mySignalWithIntArg(arg); 385 } 386 void emitMySignal2(bool arg) { 387 emit mySignal2(arg); 388 } 389 void emitMySignal2() { 390 emit mySignal2(); 391 } 392 void emitMySignalWithDateTimeArg(QDateTime dt) { 393 emit mySignalWithDateTimeArg(dt); 394 } 395 void emitMySignalWithRegexArg(QRegExp r) { 396 emit mySignalWithRegexArg(r); 397 } 398 399 public Q_SLOTS: 400 void mySlot() { 401 m_qtFunctionInvoked = 20; 402 } 403 void mySlotWithIntArg(int arg) { 404 m_qtFunctionInvoked = 21; 405 m_actuals << arg; 406 } 407 void mySlotWithDoubleArg(double arg) { 408 m_qtFunctionInvoked = 22; 409 m_actuals << arg; 410 } 411 void mySlotWithStringArg(const QString &arg) { 412 m_qtFunctionInvoked = 23; 413 m_actuals << arg; 414 } 415 416 void myOverloadedSlot() { 417 m_qtFunctionInvoked = 24; 418 } 419 void myOverloadedSlot(QObject* arg) { 420 m_qtFunctionInvoked = 41; 421 m_actuals << QVariant::fromValue(arg); 422 } 423 void myOverloadedSlot(bool arg) { 424 m_qtFunctionInvoked = 25; 425 m_actuals << arg; 426 } 427 void myOverloadedSlot(const QStringList &arg) { 428 m_qtFunctionInvoked = 42; 429 m_actuals << arg; 430 } 431 void myOverloadedSlot(double arg) { 432 m_qtFunctionInvoked = 26; 433 m_actuals << arg; 434 } 435 void myOverloadedSlot(float arg) { 436 m_qtFunctionInvoked = 27; 437 m_actuals << arg; 438 } 439 void myOverloadedSlot(int arg) { 440 m_qtFunctionInvoked = 28; 441 m_actuals << arg; 442 } 443 void myOverloadedSlot(const QString &arg) { 444 m_qtFunctionInvoked = 29; 445 m_actuals << arg; 446 } 447 void myOverloadedSlot(const QColor &arg) { 448 m_qtFunctionInvoked = 30; 449 m_actuals << arg; 450 } 451 void myOverloadedSlot(const QBrush &arg) { 452 m_qtFunctionInvoked = 31; 453 m_actuals << arg; 454 } 455 void myOverloadedSlot(const QDateTime &arg) { 456 m_qtFunctionInvoked = 32; 457 m_actuals << arg; 458 } 459 void myOverloadedSlot(const QDate &arg) { 460 m_qtFunctionInvoked = 33; 461 m_actuals << arg; 462 } 463 void myOverloadedSlot(const QRegExp &arg) { 464 m_qtFunctionInvoked = 34; 465 m_actuals << arg; 466 } 467 void myOverloadedSlot(const QVariant &arg) { 468 m_qtFunctionInvoked = 35; 469 m_actuals << arg; 470 } 471 void myOverloadedSlot(const QWebElement &arg) { 472 m_qtFunctionInvoked = 36; 473 m_actuals << QVariant::fromValue<QWebElement>(arg); 474 } 475 476 void qscript_call(int arg) { 477 m_qtFunctionInvoked = 40; 478 m_actuals << arg; 479 } 480 481 protected Q_SLOTS: 482 void myProtectedSlot() { 483 m_qtFunctionInvoked = 36; 484 } 485 486 private Q_SLOTS: 487 void myPrivateSlot() { } 488 489 Q_SIGNALS: 490 void mySignal(); 491 void mySignalWithIntArg(int arg); 492 void mySignalWithDoubleArg(double arg); 493 void mySignal2(bool arg = false); 494 void mySignalWithDateTimeArg(QDateTime dt); 495 void mySignalWithRegexArg(QRegExp r); 496 497 private: 498 int m_intValue; 499 QVariant m_variantValue; 500 QVariantList m_variantListValue; 501 QVariantMap m_variantMapValue; 502 QString m_stringValue; 503 QStringList m_stringListValue; 504 QByteArray m_byteArrayValue; 505 QBrush m_brushValue; 506 double m_hiddenValue; 507 int m_writeOnlyValue; 508 int m_readOnlyValue; 509 QKeySequence m_shortcut; 510 QWebElement m_webElement; 511 CustomType m_customType; 512 QObject* m_objectStar; 513 int m_qtFunctionInvoked; 514 QVariantList m_actuals; 515 }; 516 517 class MyWebElementSlotOnlyObject : public QObject { 518 Q_OBJECT 519 Q_PROPERTY(QString tagName READ tagName) 520 public slots: 521 void doSomethingWithWebElement(const QWebElement& element) 522 { 523 m_tagName = element.tagName(); 524 } 525 526 public: 527 QString tagName() const 528 { 529 return m_tagName; 530 } 531 private: 532 QString m_tagName; 533 }; 534 535 class MyOtherQObject : public MyQObject 536 { 537 public: 538 MyOtherQObject(QObject* parent = 0) 539 : MyQObject(parent) { } 540 }; 541 542 class MyEnumTestQObject : public QObject 543 { 544 Q_OBJECT 545 Q_PROPERTY(QString p1 READ p1) 546 Q_PROPERTY(QString p2 READ p2) 547 Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false) 548 Q_PROPERTY(QString p4 READ p4) 549 Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false) 550 Q_PROPERTY(QString p6 READ p6) 551 public: 552 MyEnumTestQObject(QObject* parent = 0) 553 : QObject(parent) { } 554 QString p1() const { 555 return QLatin1String("p1"); 556 } 557 QString p2() const { 558 return QLatin1String("p2"); 559 } 560 QString p3() const { 561 return QLatin1String("p3"); 562 } 563 QString p4() const { 564 return QLatin1String("p4"); 565 } 566 QString p5() const { 567 return QLatin1String("p5"); 568 } 569 QString p6() const { 570 return QLatin1String("p5"); 571 } 572 public Q_SLOTS: 573 void mySlot() { } 574 void myOtherSlot() { } 575 Q_SIGNALS: 576 void mySignal(); 577 }; 578 579 class tst_QWebFrame : public QObject 580 { 581 Q_OBJECT 582 583 public: 584 tst_QWebFrame(); 585 virtual ~tst_QWebFrame(); 586 bool eventFilter(QObject* watched, QEvent* event); 587 588 public slots: 589 void init(); 590 void cleanup(); 591 592 private slots: 593 void horizontalScrollAfterBack(); 594 void getSetStaticProperty(); 595 void getSetDynamicProperty(); 596 void getSetChildren(); 597 void callQtInvokable(); 598 void connectAndDisconnect(); 599 void classEnums(); 600 void classConstructor(); 601 void overrideInvokable(); 602 void transferInvokable(); 603 void findChild(); 604 void findChildren(); 605 void overloadedSlots(); 606 void webElementSlotOnly(); 607 void enumerate_data(); 608 void enumerate(); 609 void objectDeleted(); 610 void typeConversion(); 611 void arrayObjectEnumerable(); 612 void symmetricUrl(); 613 void progressSignal(); 614 void urlChange(); 615 void domCycles(); 616 void requestedUrl(); 617 void requestedUrlAfterSetAndLoadFailures(); 618 void javaScriptWindowObjectCleared_data(); 619 void javaScriptWindowObjectCleared(); 620 void javaScriptWindowObjectClearedOnEvaluate(); 621 void setHtml(); 622 void setHtmlWithResource(); 623 void setHtmlWithBaseURL(); 624 void setHtmlWithJSAlert(); 625 void ipv6HostEncoding(); 626 void metaData(); 627 #if !defined(Q_WS_MAEMO_5) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_COMBOBOX) 628 // as maemo 5 && symbian do not use QComboBoxes to implement the popups 629 // this test does not make sense for it. 630 void popupFocus(); 631 #endif 632 void inputFieldFocus(); 633 void hitTestContent(); 634 void jsByteArray(); 635 void ownership(); 636 void nullValue(); 637 void baseUrl_data(); 638 void baseUrl(); 639 void hasSetFocus(); 640 void render(); 641 void renderHints(); 642 void scrollPosition(); 643 void scrollToAnchor(); 644 void scrollbarsOff(); 645 void evaluateWillCauseRepaint(); 646 void qObjectWrapperWithSameIdentity(); 647 void introspectQtMethods_data(); 648 void introspectQtMethods(); 649 void setContent_data(); 650 void setContent(); 651 void setCacheLoadControlAttribute(); 652 void setUrlWithPendingLoads(); 653 void setUrlWithFragment_data(); 654 void setUrlWithFragment(); 655 void setUrlToEmpty(); 656 void setUrlToInvalid(); 657 void setUrlHistory(); 658 void setUrlSameUrl(); 659 void setUrlThenLoads_data(); 660 void setUrlThenLoads(); 661 662 private: 663 QString evalJS(const QString&s) { 664 // Convert an undefined return variant to the string "undefined" 665 QVariant ret = evalJSV(s); 666 if (ret.userType() == QMetaType::Void) 667 return "undefined"; 668 else 669 return ret.toString(); 670 } 671 QVariant evalJSV(const QString &s) { 672 return m_page->mainFrame()->evaluateJavaScript(s); 673 } 674 675 QString evalJS(const QString&s, QString& type) { 676 return evalJSV(s, type).toString(); 677 } 678 QVariant evalJSV(const QString &s, QString& type) { 679 // As a special measure, if we get an exception we set the type to 'error' 680 // (in ecma, an Error object has typeof object, but qtscript has a convenience function) 681 // Similarly, an array is an object, but we'd prefer to have a type of 'array' 682 // Also, consider a QMetaType::Void QVariant to be undefined 683 QString escaped = s; 684 escaped.replace('\'', "\\'"); // Don't preescape your single quotes! 685 evalJS("var retvalue;\ 686 var typevalue; \ 687 try {\ 688 retvalue = eval('" + escaped + "'); \ 689 typevalue = typeof retvalue; \ 690 if (retvalue instanceof Array) \ 691 typevalue = 'array'; \ 692 } \ 693 catch(e) {\ 694 retvalue = e.name + ': ' + e.message;\ 695 typevalue = 'error';\ 696 }"); 697 QVariant ret = evalJSV("retvalue"); 698 if (ret.userType() != QMetaType::Void) 699 type = evalJS("typevalue"); 700 else { 701 ret = QString("undefined"); 702 type = sUndefined; 703 } 704 evalJS("delete retvalue; delete typevalue"); 705 return ret; 706 } 707 QObject* firstChildByClassName(QObject* parent, const char* className) { 708 const QObjectList & children = parent->children(); 709 foreach (QObject* child, children) { 710 if (!strcmp(child->metaObject()->className(), className)) { 711 return child; 712 } 713 } 714 return 0; 715 } 716 717 const QString sTrue; 718 const QString sFalse; 719 const QString sUndefined; 720 const QString sArray; 721 const QString sFunction; 722 const QString sError; 723 const QString sString; 724 const QString sObject; 725 const QString sNumber; 726 727 private: 728 QWebView* m_view; 729 QWebPage* m_page; 730 MyQObject* m_myObject; 731 QWebView* m_inputFieldsTestView; 732 int m_inputFieldTestPaintCount; 733 }; 734 735 tst_QWebFrame::tst_QWebFrame() 736 : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"), 737 sString("string"), sObject("object"), sNumber("number"), m_inputFieldsTestView(0), m_inputFieldTestPaintCount(0) 738 { 739 } 740 741 tst_QWebFrame::~tst_QWebFrame() 742 { 743 } 744 745 bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event) 746 { 747 // used on the inputFieldFocus test 748 if (watched == m_inputFieldsTestView) { 749 if (event->type() == QEvent::Paint) 750 m_inputFieldTestPaintCount++; 751 } 752 return QObject::eventFilter(watched, event); 753 } 754 755 void tst_QWebFrame::init() 756 { 757 m_view = new QWebView(); 758 m_page = m_view->page(); 759 m_myObject = new MyQObject(); 760 m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject); 761 } 762 763 void tst_QWebFrame::cleanup() 764 { 765 delete m_view; 766 delete m_myObject; 767 } 768 769 void tst_QWebFrame::getSetStaticProperty() 770 { 771 m_page->mainFrame()->setHtml("<html><head><body></body></html>"); 772 QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined); 773 774 // initial value (set in MyQObject constructor) 775 { 776 QString type; 777 QVariant ret = evalJSV("myObject.intProperty", type); 778 QCOMPARE(type, sNumber); 779 QCOMPARE(ret.type(), QVariant::Double); 780 QCOMPARE(ret.toInt(), 123); 781 } 782 QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue); 783 784 { 785 QString type; 786 QVariant ret = evalJSV("myObject.variantProperty", type); 787 QCOMPARE(type, sString); 788 QCOMPARE(ret.type(), QVariant::String); 789 QCOMPARE(ret.toString(), QLatin1String("foo")); 790 } 791 QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue); 792 793 { 794 QString type; 795 QVariant ret = evalJSV("myObject.stringProperty", type); 796 QCOMPARE(type, sString); 797 QCOMPARE(ret.type(), QVariant::String); 798 QCOMPARE(ret.toString(), QLatin1String("bar")); 799 } 800 QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue); 801 802 { 803 QString type; 804 QVariant ret = evalJSV("myObject.variantListProperty", type); 805 QCOMPARE(type, sArray); 806 QCOMPARE(ret.type(), QVariant::List); 807 QVariantList vl = ret.value<QVariantList>(); 808 QCOMPARE(vl.size(), 2); 809 QCOMPARE(vl.at(0).toInt(), 123); 810 QCOMPARE(vl.at(1).toString(), QLatin1String("foo")); 811 } 812 QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue); 813 QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue); 814 QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue); 815 816 { 817 QString type; 818 QVariant ret = evalJSV("myObject.variantMapProperty", type); 819 QCOMPARE(type, sObject); 820 QCOMPARE(ret.type(), QVariant::Map); 821 QVariantMap vm = ret.value<QVariantMap>(); 822 QCOMPARE(vm.size(), 3); 823 QCOMPARE(vm.value("a").toInt(), 123); 824 QCOMPARE(vm.value("b").toString(), QLatin1String("foo")); 825 QCOMPARE(vm.value("c").value<QObject*>(), static_cast<QObject*>(m_myObject)); 826 } 827 QCOMPARE(evalJS("myObject.variantMapProperty.a === 123"), sTrue); 828 QCOMPARE(evalJS("myObject.variantMapProperty.b === 'foo'"), sTrue); 829 QCOMPARE(evalJS("myObject.variantMapProperty.c.variantMapProperty.b === 'foo'"), sTrue); 830 831 { 832 QString type; 833 QVariant ret = evalJSV("myObject.stringListProperty", type); 834 QCOMPARE(type, sArray); 835 QCOMPARE(ret.type(), QVariant::List); 836 QVariantList vl = ret.value<QVariantList>(); 837 QCOMPARE(vl.size(), 2); 838 QCOMPARE(vl.at(0).toString(), QLatin1String("zig")); 839 QCOMPARE(vl.at(1).toString(), QLatin1String("zag")); 840 } 841 QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue); 842 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); 843 QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig")); 844 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); 845 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag")); 846 847 // property change in C++ should be reflected in script 848 m_myObject->setIntProperty(456); 849 QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue); 850 m_myObject->setIntProperty(789); 851 QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue); 852 853 m_myObject->setVariantProperty(QLatin1String("bar")); 854 QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue); 855 m_myObject->setVariantProperty(42); 856 QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue); 857 m_myObject->setVariantProperty(QVariant::fromValue(QBrush())); 858 //XFAIL 859 // QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant); 860 861 m_myObject->setStringProperty(QLatin1String("baz")); 862 QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue); 863 m_myObject->setStringProperty(QLatin1String("zab")); 864 QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue); 865 866 // property change in script should be reflected in C++ 867 QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123")); 868 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); 869 QCOMPARE(m_myObject->intProperty(), 123); 870 QCOMPARE(evalJS("myObject.intProperty = 'ciao!';" 871 "myObject.intProperty == 0"), sTrue); 872 QCOMPARE(m_myObject->intProperty(), 0); 873 QCOMPARE(evalJS("myObject.intProperty = '123';" 874 "myObject.intProperty == 123"), sTrue); 875 QCOMPARE(m_myObject->intProperty(), 123); 876 877 QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao")); 878 QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao")); 879 QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao")); 880 QCOMPARE(evalJS("myObject.stringProperty = 123;" 881 "myObject.stringProperty"), QLatin1String("123")); 882 QCOMPARE(m_myObject->stringProperty(), QLatin1String("123")); 883 QCOMPARE(evalJS("myObject.stringProperty = null"), QString()); 884 QCOMPARE(evalJS("myObject.stringProperty"), QString()); 885 QCOMPARE(m_myObject->stringProperty(), QString()); 886 QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined); 887 QCOMPARE(evalJS("myObject.stringProperty"), QString()); 888 QCOMPARE(m_myObject->stringProperty(), QString()); 889 890 QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);" 891 "myObject.variantProperty").toDouble(), 1234.0); 892 QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0); 893 894 QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);" 895 "myObject.variantProperty"), sTrue); 896 QCOMPARE(m_myObject->variantProperty().toBool(), true); 897 898 QCOMPARE(evalJS("myObject.variantProperty = null;" 899 "myObject.variantProperty.valueOf()"), sUndefined); 900 QCOMPARE(m_myObject->variantProperty(), QVariant()); 901 QCOMPARE(evalJS("myObject.variantProperty = undefined;" 902 "myObject.variantProperty.valueOf()"), sUndefined); 903 QCOMPARE(m_myObject->variantProperty(), QVariant()); 904 905 QCOMPARE(evalJS("myObject.variantProperty = 'foo';" 906 "myObject.variantProperty.valueOf()"), QLatin1String("foo")); 907 QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo"))); 908 QCOMPARE(evalJS("myObject.variantProperty = 42;" 909 "myObject.variantProperty").toDouble(), 42.0); 910 QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0); 911 912 QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];" 913 "myObject.variantListProperty.length == 3"), sTrue); 914 QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue); 915 QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two")); 916 QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue); 917 918 QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];" 919 "myObject.stringListProperty.length == 3"), sTrue); 920 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); 921 QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue); 922 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); 923 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two")); 924 QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString); 925 QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true")); 926 evalJS("myObject.webElementProperty=document.body;"); 927 QCOMPARE(evalJS("myObject.webElementProperty.tagName"), QLatin1String("BODY")); 928 929 // try to delete 930 QCOMPARE(evalJS("delete myObject.intProperty"), sFalse); 931 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); 932 933 QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse); 934 QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0); 935 936 // custom property 937 QCOMPARE(evalJS("myObject.customProperty"), sUndefined); 938 QCOMPARE(evalJS("myObject.customProperty = 123;" 939 "myObject.customProperty == 123"), sTrue); 940 QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty"); 941 QCOMPARE(v.type(), QVariant::Double); 942 QCOMPARE(v.toInt(), 123); 943 944 // non-scriptable property 945 QCOMPARE(m_myObject->hiddenProperty(), 456.0); 946 QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined); 947 QCOMPARE(evalJS("myObject.hiddenProperty = 123;" 948 "myObject.hiddenProperty == 123"), sTrue); 949 QCOMPARE(m_myObject->hiddenProperty(), 456.0); 950 951 // write-only property 952 QCOMPARE(m_myObject->writeOnlyProperty(), 789); 953 QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined); 954 QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;" 955 "typeof myObject.writeOnlyProperty"), sUndefined); 956 QCOMPARE(m_myObject->writeOnlyProperty(), 123); 957 958 // read-only property 959 QCOMPARE(m_myObject->readOnlyProperty(), 987); 960 QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue); 961 QCOMPARE(evalJS("myObject.readOnlyProperty = 654;" 962 "myObject.readOnlyProperty == 987"), sTrue); 963 QCOMPARE(m_myObject->readOnlyProperty(), 987); 964 965 // QObject* property 966 m_myObject->setObjectStarProperty(0); 967 QCOMPARE(m_myObject->objectStarProperty(), (QObject*)0); 968 QCOMPARE(evalJS("myObject.objectStarProperty == null"), sTrue); 969 QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject); 970 QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sFalse); 971 QCOMPARE(evalJS("String(myObject.objectStarProperty) == 'null'"), sTrue); 972 QCOMPARE(evalJS("myObject.objectStarProperty.objectStarProperty"), 973 sUndefined); 974 m_myObject->setObjectStarProperty(this); 975 QCOMPARE(evalJS("myObject.objectStarProperty != null"), sTrue); 976 QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject); 977 QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sTrue); 978 QCOMPARE(evalJS("String(myObject.objectStarProperty) != 'null'"), sTrue); 979 } 980 981 void tst_QWebFrame::getSetDynamicProperty() 982 { 983 // initially the object does not have the property 984 // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty 985 986 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); 987 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 988 989 // add a dynamic property in C++ 990 QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false); 991 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue); 992 QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue); 993 QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue); 994 995 // property change in script should be reflected in C++ 996 QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';" 997 "myObject.dynamicProperty"), QLatin1String("foo")); 998 QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo")); 999 1000 // delete the property (XFAIL - can't delete properties) 1001 QEXPECT_FAIL("", "can't delete properties", Continue); 1002 QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue); 1003 /* 1004 QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false); 1005 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 1006 // QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); 1007 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 1008 */ 1009 } 1010 1011 void tst_QWebFrame::getSetChildren() 1012 { 1013 // initially the object does not have the child 1014 // (again, no hasOwnProperty) 1015 1016 //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); 1017 QCOMPARE(evalJS("typeof myObject.child"), sUndefined); 1018 1019 // add a child 1020 MyQObject* child = new MyQObject(m_myObject); 1021 child->setObjectName("child"); 1022 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue); 1023 QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue); 1024 1025 // add a grandchild 1026 MyQObject* grandChild = new MyQObject(child); 1027 grandChild->setObjectName("grandChild"); 1028 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue); 1029 QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue); 1030 1031 // delete grandchild 1032 delete grandChild; 1033 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse); 1034 QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue); 1035 1036 // delete child 1037 delete child; 1038 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); 1039 QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue); 1040 } 1041 1042 Q_DECLARE_METATYPE(QVector<int>) 1043 Q_DECLARE_METATYPE(QVector<double>) 1044 Q_DECLARE_METATYPE(QVector<QString>) 1045 1046 void tst_QWebFrame::callQtInvokable() 1047 { 1048 qRegisterMetaType<QObjectList>(); 1049 1050 m_myObject->resetQtFunctionInvoked(); 1051 QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined); 1052 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1053 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1054 1055 // extra arguments should silently be ignored 1056 m_myObject->resetQtFunctionInvoked(); 1057 QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined); 1058 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1059 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1060 1061 m_myObject->resetQtFunctionInvoked(); 1062 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined); 1063 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1064 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1065 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1066 1067 m_myObject->resetQtFunctionInvoked(); 1068 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined); 1069 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1070 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1071 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1072 1073 m_myObject->resetQtFunctionInvoked(); 1074 QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined); 1075 QCOMPARE(m_myObject->qtFunctionInvoked(), 2); 1076 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1077 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123)); 1078 1079 m_myObject->resetQtFunctionInvoked(); 1080 QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined); 1081 QCOMPARE(m_myObject->qtFunctionInvoked(), 3); 1082 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1083 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); 1084 1085 m_myObject->resetQtFunctionInvoked(); 1086 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined); 1087 QCOMPARE(m_myObject->qtFunctionInvoked(), 4); 1088 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1089 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); 1090 1091 m_myObject->resetQtFunctionInvoked(); 1092 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined); 1093 QCOMPARE(m_myObject->qtFunctionInvoked(), 4); 1094 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1095 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5); 1096 1097 m_myObject->resetQtFunctionInvoked(); 1098 QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined); 1099 QCOMPARE(m_myObject->qtFunctionInvoked(), 52); 1100 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1101 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true); 1102 1103 m_myObject->resetQtFunctionInvoked(); 1104 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined); 1105 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1106 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1107 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao")); 1108 1109 m_myObject->resetQtFunctionInvoked(); 1110 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined); 1111 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1112 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1113 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); 1114 1115 m_myObject->resetQtFunctionInvoked(); 1116 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined); 1117 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1118 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1119 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); 1120 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); 1121 1122 m_myObject->resetQtFunctionInvoked(); 1123 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined); 1124 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1125 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1126 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); 1127 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); 1128 1129 m_myObject->resetQtFunctionInvoked(); 1130 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined); 1131 QCOMPARE(m_myObject->qtFunctionInvoked(), 6); 1132 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1133 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1134 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); 1135 1136 m_myObject->resetQtFunctionInvoked(); 1137 QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123")); 1138 QCOMPARE(m_myObject->qtFunctionInvoked(), 7); 1139 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1140 1141 m_myObject->resetQtFunctionInvoked(); 1142 QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456")); 1143 QCOMPARE(m_myObject->qtFunctionInvoked(), 39); 1144 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1145 1146 m_myObject->resetQtFunctionInvoked(); 1147 QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao")); 1148 QCOMPARE(m_myObject->qtFunctionInvoked(), 8); 1149 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1150 1151 m_myObject->resetQtFunctionInvoked(); 1152 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined); 1153 QCOMPARE(m_myObject->qtFunctionInvoked(), 9); 1154 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1155 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1156 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); 1157 1158 m_myObject->resetQtFunctionInvoked(); 1159 QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined); 1160 QCOMPARE(m_myObject->qtFunctionInvoked(), 44); 1161 m_myObject->resetQtFunctionInvoked(); 1162 { 1163 QString type; 1164 QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type); 1165 QCOMPARE(type, sError); 1166 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n myInvokableWithVoidStarArg(void*)")); 1167 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1168 } 1169 1170 m_myObject->resetQtFunctionInvoked(); 1171 { 1172 QString type; 1173 QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type); 1174 QCOMPARE(type, sError); 1175 QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)")); 1176 } 1177 1178 m_myObject->resetQtFunctionInvoked(); 1179 { 1180 QString type; 1181 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type); 1182 QCOMPARE(type, sUndefined); 1183 QCOMPARE(m_myObject->qtFunctionInvoked(), 47); 1184 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1185 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1186 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello")); 1187 } 1188 1189 m_myObject->resetQtFunctionInvoked(); 1190 { 1191 QString type; 1192 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type); 1193 QCOMPARE(type, sUndefined); 1194 QCOMPARE(m_myObject->qtFunctionInvoked(), 47); 1195 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1196 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); 1197 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString()); 1198 } 1199 1200 // calling function that returns (const)ref 1201 m_myObject->resetQtFunctionInvoked(); 1202 { 1203 QString type; 1204 QString ret = evalJS("typeof myObject.myInvokableReturningRef()"); 1205 QCOMPARE(ret, sUndefined); 1206 //QVERIFY(!m_engine->hasUncaughtException()); 1207 QCOMPARE(m_myObject->qtFunctionInvoked(), 48); 1208 } 1209 1210 m_myObject->resetQtFunctionInvoked(); 1211 { 1212 QString type; 1213 QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()"); 1214 QCOMPARE(ret, sUndefined); 1215 //QVERIFY(!m_engine->hasUncaughtException()); 1216 QCOMPARE(m_myObject->qtFunctionInvoked(), 49); 1217 } 1218 1219 m_myObject->resetQtFunctionInvoked(); 1220 { 1221 QString type; 1222 QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type); 1223 QCOMPARE(m_myObject->qtFunctionInvoked(), 13); 1224 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1225 QCOMPARE(type, sObject); 1226 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); 1227 } 1228 1229 m_myObject->resetQtFunctionInvoked(); 1230 { 1231 QString type; 1232 QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type); 1233 QCOMPARE(m_myObject->qtFunctionInvoked(), 14); 1234 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1235 QCOMPARE(type, sArray); 1236 QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList 1237 QVariantList vl = qvariant_cast<QVariantList>(ret); 1238 QCOMPARE(vl.count(), 1); 1239 } 1240 1241 m_myObject->resetQtFunctionInvoked(); 1242 { 1243 QString type; 1244 m_myObject->setVariantProperty(QVariant(123)); 1245 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type); 1246 QCOMPARE(type, sNumber); 1247 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1248 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1249 QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty()); 1250 QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int 1251 QCOMPARE(ret.toInt(),123); 1252 } 1253 1254 m_myObject->resetQtFunctionInvoked(); 1255 { 1256 QString type; 1257 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type); 1258 QCOMPARE(type, sObject); 1259 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1260 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1261 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); 1262 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); 1263 } 1264 1265 m_myObject->resetQtFunctionInvoked(); 1266 { 1267 QString type; 1268 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type); 1269 QCOMPARE(type, sObject); 1270 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1271 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1272 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); 1273 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); 1274 } 1275 1276 /* XFAIL - variant support 1277 m_myObject->resetQtFunctionInvoked(); 1278 { 1279 m_myObject->setVariantProperty(QVariant::fromValue(QBrush())); 1280 QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)"); 1281 QVERIFY(ret.isVariant()); 1282 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1283 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1284 QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0)); 1285 QCOMPARE(ret.toVariant(), m_myObject->variantProperty()); 1286 } 1287 */ 1288 1289 m_myObject->resetQtFunctionInvoked(); 1290 { 1291 QString type; 1292 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type); 1293 QCOMPARE(type, sNumber); 1294 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1295 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1296 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123)); 1297 QCOMPARE(ret.userType(), int(QMetaType::Double)); 1298 QCOMPARE(ret.toInt(),123); 1299 } 1300 1301 m_myObject->resetQtFunctionInvoked(); 1302 { 1303 QString type; 1304 QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type); 1305 QCOMPARE(m_myObject->qtFunctionInvoked(), 16); 1306 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1307 1308 QVariant v = m_myObject->qtFunctionActuals().at(0); 1309 QCOMPARE(v.userType(), int(QMetaType::QVariantMap)); 1310 1311 QVariantMap vmap = qvariant_cast<QVariantMap>(v); 1312 QCOMPARE(vmap.keys().size(), 2); 1313 QCOMPARE(vmap.keys().at(0), QLatin1String("a")); 1314 QCOMPARE(vmap.value("a"), QVariant(123)); 1315 QCOMPARE(vmap.keys().at(1), QLatin1String("b")); 1316 QCOMPARE(vmap.value("b"), QVariant("ciao")); 1317 1318 QCOMPARE(type, sObject); 1319 1320 QCOMPARE(ret.userType(), int(QMetaType::QVariantMap)); 1321 vmap = qvariant_cast<QVariantMap>(ret); 1322 QCOMPARE(vmap.keys().size(), 2); 1323 QCOMPARE(vmap.keys().at(0), QLatin1String("a")); 1324 QCOMPARE(vmap.value("a"), QVariant(123)); 1325 QCOMPARE(vmap.keys().at(1), QLatin1String("b")); 1326 QCOMPARE(vmap.value("b"), QVariant("ciao")); 1327 } 1328 1329 m_myObject->resetQtFunctionInvoked(); 1330 { 1331 QString type; 1332 QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type); 1333 QCOMPARE(m_myObject->qtFunctionInvoked(), 17); 1334 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1335 QVariant v = m_myObject->qtFunctionActuals().at(0); 1336 QCOMPARE(v.userType(), qMetaTypeId<QList<int> >()); 1337 QList<int> ilst = qvariant_cast<QList<int> >(v); 1338 QCOMPARE(ilst.size(), 2); 1339 QCOMPARE(ilst.at(0), 1); 1340 QCOMPARE(ilst.at(1), 5); 1341 1342 QCOMPARE(type, sArray); 1343 QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist 1344 QVariantList vlst = qvariant_cast<QVariantList>(ret); 1345 QCOMPARE(vlst.size(), 2); 1346 QCOMPARE(vlst.at(0).toInt(), 1); 1347 QCOMPARE(vlst.at(1).toInt(), 5); 1348 } 1349 1350 m_myObject->resetQtFunctionInvoked(); 1351 { 1352 QString type; 1353 QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type); 1354 QCOMPARE(m_myObject->qtFunctionInvoked(), 18); 1355 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1356 QVariant v = m_myObject->qtFunctionActuals().at(0); 1357 QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); 1358 QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject); 1359 1360 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); 1361 QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject); 1362 1363 QCOMPARE(type, sObject); 1364 } 1365 1366 m_myObject->resetQtFunctionInvoked(); 1367 { 1368 // no implicit conversion from integer to QObject* 1369 QString type; 1370 evalJS("myObject.myInvokableWithQObjectStarArg(123)", type); 1371 QCOMPARE(type, sError); 1372 } 1373 1374 /* 1375 m_myObject->resetQtFunctionInvoked(); 1376 { 1377 QString fun = evalJS("myObject.myInvokableWithQBrushArg"); 1378 Q_ASSERT(fun.isFunction()); 1379 QColor color(10, 20, 30, 40); 1380 // QColor should be converted to a QBrush 1381 QVariant ret = fun.call(QString(), QStringList() 1382 << qScriptValueFromValue(m_engine, color)); 1383 QCOMPARE(m_myObject->qtFunctionInvoked(), 19); 1384 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1385 QVariant v = m_myObject->qtFunctionActuals().at(0); 1386 QCOMPARE(v.userType(), int(QMetaType::QBrush)); 1387 QCOMPARE(qvariant_cast<QColor>(v), color); 1388 1389 QCOMPARE(qscriptvalue_cast<QColor>(ret), color); 1390 } 1391 */ 1392 1393 // private slots should not be part of the QObject binding 1394 QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined); 1395 1396 // protected slots should be fine 1397 m_myObject->resetQtFunctionInvoked(); 1398 evalJS("myObject.myProtectedSlot()"); 1399 QCOMPARE(m_myObject->qtFunctionInvoked(), 36); 1400 1401 // call with too few arguments 1402 { 1403 QString type; 1404 QString ret = evalJS("myObject.myInvokableWithIntArg()", type); 1405 QCOMPARE(type, sError); 1406 QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)")); 1407 } 1408 1409 // call function where not all types have been registered 1410 m_myObject->resetQtFunctionInvoked(); 1411 { 1412 QString type; 1413 QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type); 1414 QCOMPARE(type, sError); 1415 QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'")); 1416 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1417 } 1418 1419 // call function with incompatible argument type 1420 m_myObject->resetQtFunctionInvoked(); 1421 { 1422 QString type; 1423 QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type); 1424 QCOMPARE(type, sError); 1425 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)")); 1426 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1427 } 1428 } 1429 1430 void tst_QWebFrame::connectAndDisconnect() 1431 { 1432 // connect(function) 1433 QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction); 1434 QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction); 1435 QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction); 1436 1437 { 1438 QString type; 1439 evalJS("myObject.mySignal.connect(123)", type); 1440 QCOMPARE(type, sError); 1441 } 1442 1443 evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }"); 1444 1445 QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined); 1446 1447 evalJS("gotSignal = false"); 1448 evalJS("myObject.mySignal()"); 1449 QCOMPARE(evalJS("gotSignal"), sTrue); 1450 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1451 QCOMPARE(evalJS("signalSender"),evalJS("myObject")); 1452 QCOMPARE(evalJS("slotThisObject == window"), sTrue); 1453 1454 evalJS("gotSignal = false"); 1455 m_myObject->emitMySignal(); 1456 QCOMPARE(evalJS("gotSignal"), sTrue); 1457 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1458 1459 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined); 1460 1461 evalJS("gotSignal = false"); 1462 m_myObject->emitMySignalWithIntArg(123); 1463 QCOMPARE(evalJS("gotSignal"), sTrue); 1464 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1465 QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue); 1466 1467 QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined); 1468 { 1469 QString type; 1470 evalJS("myObject.mySignal.disconnect(myHandler)", type); 1471 QCOMPARE(type, sError); 1472 } 1473 1474 evalJS("gotSignal = false"); 1475 QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined); 1476 m_myObject->emitMySignal2(true); 1477 QCOMPARE(evalJS("gotSignal"), sTrue); 1478 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1479 QCOMPARE(evalJS("signalArgs[0]"), sTrue); 1480 1481 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined); 1482 1483 QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction); 1484 QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction); 1485 QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction); 1486 1487 QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined); 1488 1489 evalJS("gotSignal = false"); 1490 m_myObject->emitMySignal2(); 1491 QCOMPARE(evalJS("gotSignal"), sTrue); 1492 1493 QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined); 1494 1495 // connect(object, function) 1496 evalJS("otherObject = { name:'foo' }"); 1497 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); 1498 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); 1499 evalJS("gotSignal = false"); 1500 m_myObject->emitMySignal(); 1501 QCOMPARE(evalJS("gotSignal"), sFalse); 1502 1503 { 1504 QString type; 1505 evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type); 1506 QCOMPARE(type, sError); 1507 } 1508 1509 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); 1510 evalJS("gotSignal = false"); 1511 m_myObject->emitMySignal(); 1512 QCOMPARE(evalJS("gotSignal"), sTrue); 1513 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1514 QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject")); 1515 QCOMPARE(evalJS("signalSender"),evalJS("myObject")); 1516 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo")); 1517 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); 1518 1519 evalJS("yetAnotherObject = { name:'bar', func : function() { } }"); 1520 QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined); 1521 evalJS("gotSignal = false"); 1522 m_myObject->emitMySignal2(true); 1523 QCOMPARE(evalJS("gotSignal"), sTrue); 1524 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1525 QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue); 1526 QCOMPARE(evalJS("signalSender == myObject"), sTrue); 1527 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar")); 1528 QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined); 1529 1530 QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined); 1531 evalJS("gotSignal = false"); 1532 m_myObject->emitMySignal2(true); 1533 QCOMPARE(evalJS("gotSignal"), sTrue); 1534 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1535 QCOMPARE(evalJS("slotThisObject == myObject"), sTrue); 1536 QCOMPARE(evalJS("signalSender == myObject"), sTrue); 1537 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined); 1538 1539 // connect(obj, string) 1540 QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined); 1541 QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined); 1542 QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined); 1543 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined); 1544 1545 // check that emitting signals from script works 1546 1547 // no arguments 1548 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); 1549 m_myObject->resetQtFunctionInvoked(); 1550 QCOMPARE(evalJS("myObject.mySignal()"), sUndefined); 1551 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1552 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined); 1553 1554 // one argument 1555 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined); 1556 m_myObject->resetQtFunctionInvoked(); 1557 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1558 QCOMPARE(m_myObject->qtFunctionInvoked(), 21); 1559 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1560 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1561 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined); 1562 1563 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined); 1564 m_myObject->resetQtFunctionInvoked(); 1565 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1566 QCOMPARE(m_myObject->qtFunctionInvoked(), 22); 1567 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1568 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0); 1569 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined); 1570 1571 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined); 1572 m_myObject->resetQtFunctionInvoked(); 1573 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1574 QCOMPARE(m_myObject->qtFunctionInvoked(), 23); 1575 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1576 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); 1577 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined); 1578 1579 // connecting to overloaded slot 1580 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined); 1581 m_myObject->resetQtFunctionInvoked(); 1582 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1583 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload 1584 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1585 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1586 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined); 1587 1588 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined); 1589 m_myObject->resetQtFunctionInvoked(); 1590 QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined); 1591 QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload 1592 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1593 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); 1594 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined); 1595 1596 // erroneous input 1597 { 1598 // ### QtScript adds .connect to all functions, WebKit does only to signals/slots 1599 QString type; 1600 QString ret = evalJS("(function() { }).connect()", type); 1601 QCOMPARE(type, sError); 1602 QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); 1603 } 1604 { 1605 QString type; 1606 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect()", type); 1607 QCOMPARE(type, sError); 1608 QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); 1609 } 1610 1611 { 1612 QString type; 1613 QString ret = evalJS("(function() { }).connect(123)", type); 1614 QCOMPARE(type, sError); 1615 QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); 1616 } 1617 { 1618 QString type; 1619 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect(123)", type); 1620 QCOMPARE(type, sError); 1621 QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); 1622 } 1623 1624 { 1625 QString type; 1626 QString ret = evalJS("myObject.myInvokable.connect(123)", type); 1627 QCOMPARE(type, sError); 1628 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); 1629 } 1630 { 1631 QString type; 1632 QString ret = evalJS("myObject.myInvokable.connect(function() { })", type); 1633 QCOMPARE(type, sError); 1634 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); 1635 } 1636 1637 { 1638 QString type; 1639 QString ret = evalJS("myObject.mySignal.connect(123)", type); 1640 QCOMPARE(type, sError); 1641 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function")); 1642 } 1643 1644 { 1645 QString type; 1646 QString ret = evalJS("myObject.mySignal.disconnect()", type); 1647 QCOMPARE(type, sError); 1648 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); 1649 } 1650 { 1651 QString type; 1652 QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect; o.disconnect()", type); 1653 QCOMPARE(type, sError); 1654 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); 1655 } 1656 1657 /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots 1658 { 1659 QString type; 1660 QString ret = evalJS("(function() { }).disconnect(123)", type); 1661 QCOMPARE(type, sError); 1662 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal")); 1663 } 1664 */ 1665 1666 { 1667 QString type; 1668 QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type); 1669 QCOMPARE(type, sError); 1670 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1671 } 1672 1673 { 1674 QString type; 1675 QString ret = evalJS("myObject.myInvokable.disconnect(123)", type); 1676 QCOMPARE(type, sError); 1677 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1678 } 1679 { 1680 QString type; 1681 QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type); 1682 QCOMPARE(type, sError); 1683 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1684 } 1685 1686 { 1687 QString type; 1688 QString ret = evalJS("myObject.mySignal.disconnect(123)", type); 1689 QCOMPARE(type, sError); 1690 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function")); 1691 } 1692 1693 { 1694 QString type; 1695 QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type); 1696 QCOMPARE(type, sError); 1697 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()")); 1698 } 1699 1700 // when the wrapper dies, the connection stays alive 1701 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); 1702 m_myObject->resetQtFunctionInvoked(); 1703 m_myObject->emitMySignal(); 1704 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1705 evalJS("myObject = null"); 1706 evalJS("gc()"); 1707 m_myObject->resetQtFunctionInvoked(); 1708 m_myObject->emitMySignal(); 1709 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1710 } 1711 1712 void tst_QWebFrame::classEnums() 1713 { 1714 // We don't do the meta thing currently, unfortunately!!! 1715 /* 1716 QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue()); 1717 m_engine->globalObject().setProperty("MyQObject", myClass); 1718 1719 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()), 1720 MyQObject::FooPolicy); 1721 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()), 1722 MyQObject::BarPolicy); 1723 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()), 1724 MyQObject::BazPolicy); 1725 1726 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()), 1727 MyQObject::FooStrategy); 1728 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()), 1729 MyQObject::BarStrategy); 1730 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()), 1731 MyQObject::BazStrategy); 1732 1733 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()), 1734 MyQObject::NoAbility); 1735 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()), 1736 MyQObject::FooAbility); 1737 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()), 1738 MyQObject::BarAbility); 1739 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()), 1740 MyQObject::BazAbility); 1741 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()), 1742 MyQObject::AllAbility); 1743 1744 // enums from Qt are inherited through prototype 1745 QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()), 1746 Qt::StrongFocus); 1747 QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()), 1748 Qt::Key_Left); 1749 1750 QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject")); 1751 1752 qRegisterMetaType<MyQObject::Policy>("Policy"); 1753 1754 m_myObject->resetQtFunctionInvoked(); 1755 QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined); 1756 QCOMPARE(m_myObject->qtFunctionInvoked(), 10); 1757 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1758 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); 1759 1760 m_myObject->resetQtFunctionInvoked(); 1761 QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined); 1762 QCOMPARE(m_myObject->qtFunctionInvoked(), 36); 1763 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1764 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); 1765 1766 m_myObject->resetQtFunctionInvoked(); 1767 { 1768 QVariant ret = evalJS("myObject.myInvokableReturningEnum()"); 1769 QCOMPARE(m_myObject->qtFunctionInvoked(), 37); 1770 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1771 QCOMPARE(ret.isVariant()); 1772 } 1773 m_myObject->resetQtFunctionInvoked(); 1774 { 1775 QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()"); 1776 QCOMPARE(m_myObject->qtFunctionInvoked(), 38); 1777 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1778 QCOMPARE(ret.isNumber()); 1779 } 1780 */ 1781 } 1782 1783 void tst_QWebFrame::classConstructor() 1784 { 1785 /* 1786 QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine); 1787 m_engine->globalObject().setProperty("MyQObject", myClass); 1788 1789 QString myObj = evalJS("myObj = MyQObject()"); 1790 QObject* qobj = myObj.toQObject(); 1791 QVERIFY(qobj != 0); 1792 QCOMPARE(qobj->metaObject()->className(), "MyQObject"); 1793 QCOMPARE(qobj->parent(), (QObject*)0); 1794 1795 QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine); 1796 m_engine->globalObject().setProperty("QObject", qobjectClass); 1797 1798 QString otherObj = evalJS("otherObj = QObject(myObj)"); 1799 QObject* qqobj = otherObj.toQObject(); 1800 QVERIFY(qqobj != 0); 1801 QCOMPARE(qqobj->metaObject()->className(), "QObject"); 1802 QCOMPARE(qqobj->parent(), qobj); 1803 1804 delete qobj; 1805 */ 1806 } 1807 1808 void tst_QWebFrame::overrideInvokable() 1809 { 1810 m_myObject->resetQtFunctionInvoked(); 1811 QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined); 1812 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1813 1814 /* XFAIL - can't write to functions with RuntimeObject 1815 m_myObject->resetQtFunctionInvoked(); 1816 evalJS("myObject.myInvokable = function() { window.a = 123; }"); 1817 evalJS("myObject.myInvokable()"); 1818 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1819 QCOMPARE(evalJS("window.a").toDouble(), 123.0); 1820 1821 evalJS("myObject.myInvokable = function() { window.a = 456; }"); 1822 evalJS("myObject.myInvokable()"); 1823 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1824 QCOMPARE(evalJS("window.a").toDouble(), 456.0); 1825 */ 1826 1827 evalJS("delete myObject.myInvokable"); 1828 evalJS("myObject.myInvokable()"); 1829 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1830 1831 /* XFAIL - ditto 1832 m_myObject->resetQtFunctionInvoked(); 1833 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); 1834 evalJS("myObject.myInvokable(123)"); 1835 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1836 */ 1837 1838 evalJS("delete myObject.myInvokable"); 1839 m_myObject->resetQtFunctionInvoked(); 1840 // this form (with the '()') is read-only 1841 evalJS("myObject['myInvokable()'] = function() { window.a = 123; }"); 1842 evalJS("myObject.myInvokable()"); 1843 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1844 } 1845 1846 void tst_QWebFrame::transferInvokable() 1847 { 1848 /* XFAIL - can't put to functions with RuntimeObject 1849 m_myObject->resetQtFunctionInvoked(); 1850 evalJS("myObject.foozball = myObject.myInvokable"); 1851 evalJS("myObject.foozball()"); 1852 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1853 m_myObject->resetQtFunctionInvoked(); 1854 evalJS("myObject.foozball = myObject.myInvokableWithIntArg"); 1855 evalJS("myObject.foozball(123)"); 1856 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1857 m_myObject->resetQtFunctionInvoked(); 1858 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); 1859 evalJS("myObject.myInvokable(123)"); 1860 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1861 1862 MyOtherQObject other; 1863 m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other); 1864 evalJS("myOtherObject.foo = myObject.foozball"); 1865 other.resetQtFunctionInvoked(); 1866 evalJS("myOtherObject.foo(456)"); 1867 QCOMPARE(other.qtFunctionInvoked(), 1); 1868 */ 1869 } 1870 1871 void tst_QWebFrame::findChild() 1872 { 1873 /* 1874 QObject* child = new QObject(m_myObject); 1875 child->setObjectName(QLatin1String("myChildObject")); 1876 1877 { 1878 QString result = evalJS("myObject.findChild('noSuchChild')"); 1879 QCOMPARE(result.isNull()); 1880 } 1881 1882 { 1883 QString result = evalJS("myObject.findChild('myChildObject')"); 1884 QCOMPARE(result.isQObject()); 1885 QCOMPARE(result.toQObject(), child); 1886 } 1887 1888 delete child; 1889 */ 1890 } 1891 1892 void tst_QWebFrame::findChildren() 1893 { 1894 /* 1895 QObject* child = new QObject(m_myObject); 1896 child->setObjectName(QLatin1String("myChildObject")); 1897 1898 { 1899 QString result = evalJS("myObject.findChildren('noSuchChild')"); 1900 QCOMPARE(result.isArray()); 1901 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0); 1902 } 1903 1904 { 1905 QString result = evalJS("myObject.findChildren('myChildObject')"); 1906 QCOMPARE(result.isArray()); 1907 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1908 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); 1909 } 1910 1911 QObject* namelessChild = new QObject(m_myObject); 1912 1913 { 1914 QString result = evalJS("myObject.findChildren('myChildObject')"); 1915 QCOMPARE(result.isArray()); 1916 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1917 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); 1918 } 1919 1920 QObject* anotherChild = new QObject(m_myObject); 1921 anotherChild->setObjectName(QLatin1String("anotherChildObject")); 1922 1923 { 1924 QString result = evalJS("myObject.findChildren('anotherChildObject')"); 1925 QCOMPARE(result.isArray()); 1926 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1927 QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild); 1928 } 1929 1930 anotherChild->setObjectName(QLatin1String("myChildObject")); 1931 { 1932 QString result = evalJS("myObject.findChildren('myChildObject')"); 1933 QCOMPARE(result.isArray()); 1934 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0); 1935 QObject* o1 = result.property(QLatin1String("0")).toQObject(); 1936 QObject* o2 = result.property(QLatin1String("1")).toQObject(); 1937 if (o1 != child) { 1938 QCOMPARE(o1, anotherChild); 1939 QCOMPARE(o2, child); 1940 } else { 1941 QCOMPARE(o1, child); 1942 QCOMPARE(o2, anotherChild); 1943 } 1944 } 1945 1946 // find all 1947 { 1948 QString result = evalJS("myObject.findChildren()"); 1949 QVERIFY(result.isArray()); 1950 int count = 3; 1951 QCOMPARE(result.property("length"), QLatin1String(count); 1952 for (int i = 0; i < 3; ++i) { 1953 QObject* o = result.property(i).toQObject(); 1954 if (o == namelessChild || o == child || o == anotherChild) 1955 --count; 1956 } 1957 QVERIFY(count == 0); 1958 } 1959 1960 delete anotherChild; 1961 delete namelessChild; 1962 delete child; 1963 */ 1964 } 1965 1966 void tst_QWebFrame::overloadedSlots() 1967 { 1968 // should pick myOverloadedSlot(double) 1969 m_myObject->resetQtFunctionInvoked(); 1970 evalJS("myObject.myOverloadedSlot(10)"); 1971 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); 1972 1973 // should pick myOverloadedSlot(double) 1974 m_myObject->resetQtFunctionInvoked(); 1975 evalJS("myObject.myOverloadedSlot(10.0)"); 1976 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); 1977 1978 // should pick myOverloadedSlot(QString) 1979 m_myObject->resetQtFunctionInvoked(); 1980 evalJS("myObject.myOverloadedSlot('10')"); 1981 QCOMPARE(m_myObject->qtFunctionInvoked(), 29); 1982 1983 // should pick myOverloadedSlot(bool) 1984 m_myObject->resetQtFunctionInvoked(); 1985 evalJS("myObject.myOverloadedSlot(true)"); 1986 QCOMPARE(m_myObject->qtFunctionInvoked(), 25); 1987 1988 // should pick myOverloadedSlot(QDateTime) 1989 m_myObject->resetQtFunctionInvoked(); 1990 evalJS("myObject.myOverloadedSlot(new Date())"); 1991 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 1992 1993 // should pick myOverloadedSlot(QRegExp) 1994 m_myObject->resetQtFunctionInvoked(); 1995 evalJS("myObject.myOverloadedSlot(new RegExp())"); 1996 QCOMPARE(m_myObject->qtFunctionInvoked(), 34); 1997 1998 // should pick myOverloadedSlot(QVariant) 1999 /* XFAIL 2000 m_myObject->resetQtFunctionInvoked(); 2001 QString f = evalJS("myObject.myOverloadedSlot"); 2002 f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao"))); 2003 QCOMPARE(m_myObject->qtFunctionInvoked(), 35); 2004 */ 2005 2006 // should pick myOverloadedSlot(QRegExp) 2007 m_myObject->resetQtFunctionInvoked(); 2008 evalJS("myObject.myOverloadedSlot(document.body)"); 2009 QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=37319", Continue); 2010 QCOMPARE(m_myObject->qtFunctionInvoked(), 36); 2011 2012 // should pick myOverloadedSlot(QObject*) 2013 m_myObject->resetQtFunctionInvoked(); 2014 evalJS("myObject.myOverloadedSlot(myObject)"); 2015 QCOMPARE(m_myObject->qtFunctionInvoked(), 41); 2016 2017 // should pick myOverloadedSlot(QObject*) 2018 m_myObject->resetQtFunctionInvoked(); 2019 evalJS("myObject.myOverloadedSlot(null)"); 2020 QCOMPARE(m_myObject->qtFunctionInvoked(), 41); 2021 2022 // should pick myOverloadedSlot(QStringList) 2023 m_myObject->resetQtFunctionInvoked(); 2024 evalJS("myObject.myOverloadedSlot(['hello'])"); 2025 QCOMPARE(m_myObject->qtFunctionInvoked(), 42); 2026 } 2027 2028 void tst_QWebFrame::enumerate_data() 2029 { 2030 QTest::addColumn<QStringList>("expectedNames"); 2031 2032 QTest::newRow("enumerate all") 2033 << (QStringList() 2034 // meta-object-defined properties: 2035 // inherited 2036 << "objectName" 2037 // non-inherited 2038 << "p1" << "p2" << "p4" << "p6" 2039 // dynamic properties 2040 << "dp1" << "dp2" << "dp3" 2041 // inherited slots 2042 << "destroyed(QObject*)" << "destroyed()" 2043 << "deleteLater()" 2044 // not included because it's private: 2045 // << "_q_reregisterTimers(void*)" 2046 // signals 2047 << "mySignal()" 2048 // slots 2049 << "mySlot()" << "myOtherSlot()"); 2050 } 2051 2052 void tst_QWebFrame::enumerate() 2053 { 2054 QFETCH(QStringList, expectedNames); 2055 2056 MyEnumTestQObject enumQObject; 2057 // give it some dynamic properties 2058 enumQObject.setProperty("dp1", "dp1"); 2059 enumQObject.setProperty("dp2", "dp2"); 2060 enumQObject.setProperty("dp3", "dp3"); 2061 m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject); 2062 2063 // enumerate in script 2064 { 2065 evalJS("var enumeratedProperties = []"); 2066 evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }"); 2067 QStringList result = evalJSV("enumeratedProperties").toStringList(); 2068 QCOMPARE(result.size(), expectedNames.size()); 2069 for (int i = 0; i < expectedNames.size(); ++i) 2070 QCOMPARE(result.at(i), expectedNames.at(i)); 2071 } 2072 } 2073 2074 void tst_QWebFrame::objectDeleted() 2075 { 2076 MyQObject* qobj = new MyQObject(); 2077 m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj); 2078 evalJS("bar.objectName = 'foo';"); 2079 QCOMPARE(qobj->objectName(), QLatin1String("foo")); 2080 evalJS("bar.intProperty = 123;"); 2081 QCOMPARE(qobj->intProperty(), 123); 2082 qobj->resetQtFunctionInvoked(); 2083 evalJS("bar.myInvokable.call(bar);"); 2084 QCOMPARE(qobj->qtFunctionInvoked(), 0); 2085 2086 // do this, to ensure that we cache that it implements call 2087 evalJS("bar()"); 2088 2089 // now delete the object 2090 delete qobj; 2091 2092 QCOMPARE(evalJS("typeof bar"), sObject); 2093 2094 // any attempt to access properties of the object should result in an exception 2095 { 2096 QString type; 2097 QString ret = evalJS("bar.objectName", type); 2098 QCOMPARE(type, sError); 2099 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 2100 } 2101 { 2102 QString type; 2103 QString ret = evalJS("bar.objectName = 'foo'", type); 2104 QCOMPARE(type, sError); 2105 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 2106 } 2107 2108 // myInvokable is stored in member table (since we've accessed it before deletion) 2109 { 2110 QString type; 2111 evalJS("bar.myInvokable", type); 2112 QCOMPARE(type, sFunction); 2113 } 2114 2115 { 2116 QString type; 2117 QString ret = evalJS("bar.myInvokable.call(bar);", type); 2118 ret = evalJS("bar.myInvokable(bar)", type); 2119 QCOMPARE(type, sError); 2120 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); 2121 } 2122 // myInvokableWithIntArg is not stored in member table (since we've not accessed it) 2123 { 2124 QString type; 2125 QString ret = evalJS("bar.myInvokableWithIntArg", type); 2126 QCOMPARE(type, sError); 2127 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); 2128 } 2129 2130 // access from script 2131 evalJS("window.o = bar;"); 2132 { 2133 QString type; 2134 QString ret = evalJS("o.objectName", type); 2135 QCOMPARE(type, sError); 2136 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 2137 } 2138 { 2139 QString type; 2140 QString ret = evalJS("o.myInvokable()", type); 2141 QCOMPARE(type, sError); 2142 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); 2143 } 2144 { 2145 QString type; 2146 QString ret = evalJS("o.myInvokableWithIntArg(10)", type); 2147 QCOMPARE(type, sError); 2148 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); 2149 } 2150 } 2151 2152 void tst_QWebFrame::typeConversion() 2153 { 2154 m_myObject->resetQtFunctionInvoked(); 2155 2156 QDateTime localdt(QDate(2008,1,18), QTime(12,31,0)); 2157 QDateTime utclocaldt = localdt.toUTC(); 2158 QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC); 2159 2160 // Dates in JS (default to local) 2161 evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))"); 2162 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 2163 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 2164 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt); 2165 2166 m_myObject->resetQtFunctionInvoked(); 2167 evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))"); 2168 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 2169 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 2170 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt); 2171 2172 // Pushing QDateTimes into JS 2173 // Local 2174 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}"); 2175 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); 2176 m_myObject->emitMySignalWithDateTimeArg(localdt); 2177 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2178 evalJS("delete window.__date_equals"); 2179 m_myObject->emitMySignalWithDateTimeArg(utclocaldt); 2180 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2181 evalJS("delete window.__date_equals"); 2182 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); 2183 2184 // UTC 2185 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }"); 2186 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); 2187 m_myObject->emitMySignalWithDateTimeArg(utcdt); 2188 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2189 evalJS("delete window.__date_equals"); 2190 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); 2191 2192 // ### RegExps 2193 } 2194 2195 class StringListTestObject : public QObject { 2196 Q_OBJECT 2197 public Q_SLOTS: 2198 QVariant stringList() 2199 { 2200 return QStringList() << "Q" << "t"; 2201 }; 2202 }; 2203 2204 void tst_QWebFrame::arrayObjectEnumerable() 2205 { 2206 QWebPage page; 2207 QWebFrame* frame = page.mainFrame(); 2208 QObject* qobject = new StringListTestObject(); 2209 frame->addToJavaScriptWindowObject("test", qobject, QScriptEngine::ScriptOwnership); 2210 2211 const QString script("var stringArray = test.stringList();" 2212 "var result = '';" 2213 "for (var i in stringArray) {" 2214 " result += stringArray[i];" 2215 "}" 2216 "result;"); 2217 QCOMPARE(frame->evaluateJavaScript(script).toString(), QString::fromLatin1("Qt")); 2218 } 2219 2220 void tst_QWebFrame::symmetricUrl() 2221 { 2222 QVERIFY(m_view->url().isEmpty()); 2223 2224 QCOMPARE(m_view->history()->count(), 0); 2225 2226 QUrl dataUrl("data:text/html,<h1>Test"); 2227 2228 m_view->setUrl(dataUrl); 2229 QCOMPARE(m_view->url(), dataUrl); 2230 QCOMPARE(m_view->history()->count(), 0); 2231 2232 // loading is _not_ immediate, so the text isn't set just yet. 2233 QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty()); 2234 2235 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2236 2237 QCOMPARE(m_view->history()->count(), 1); 2238 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test")); 2239 2240 QUrl dataUrl2("data:text/html,<h1>Test2"); 2241 QUrl dataUrl3("data:text/html,<h1>Test3"); 2242 2243 m_view->setUrl(dataUrl2); 2244 m_view->setUrl(dataUrl3); 2245 2246 QCOMPARE(m_view->url(), dataUrl3); 2247 2248 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2249 2250 QCOMPARE(m_view->history()->count(), 2); 2251 2252 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3")); 2253 } 2254 2255 void tst_QWebFrame::progressSignal() 2256 { 2257 QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); 2258 2259 QUrl dataUrl("data:text/html,<h1>Test"); 2260 m_view->setUrl(dataUrl); 2261 2262 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2263 2264 QVERIFY(progressSpy.size() >= 2); 2265 2266 // WebKit defines initialProgressValue as 10%, not 0% 2267 QCOMPARE(progressSpy.first().first().toInt(), 10); 2268 2269 // But we always end at 100% 2270 QCOMPARE(progressSpy.last().first().toInt(), 100); 2271 } 2272 2273 void tst_QWebFrame::urlChange() 2274 { 2275 QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2276 2277 QUrl dataUrl("data:text/html,<h1>Test"); 2278 m_view->setUrl(dataUrl); 2279 2280 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2281 2282 QCOMPARE(urlSpy.size(), 1); 2283 2284 QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); 2285 m_view->setUrl(dataUrl2); 2286 2287 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2288 2289 QCOMPARE(urlSpy.size(), 2); 2290 } 2291 2292 2293 void tst_QWebFrame::domCycles() 2294 { 2295 m_view->setHtml("<html><body>"); 2296 QVariant v = m_page->mainFrame()->evaluateJavaScript("document"); 2297 QVERIFY(v.type() == QVariant::Map); 2298 } 2299 2300 class FakeReply : public QNetworkReply { 2301 Q_OBJECT 2302 2303 public: 2304 FakeReply(const QNetworkRequest& request, QObject* parent = 0) 2305 : QNetworkReply(parent) 2306 { 2307 setOperation(QNetworkAccessManager::GetOperation); 2308 setRequest(request); 2309 if (request.url() == QUrl("qrc:/test1.html")) { 2310 setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); 2311 setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); 2312 } 2313 #ifndef QT_NO_OPENSSL 2314 else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) 2315 setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error 2316 #endif 2317 else if (request.url().host() == QLatin1String("abcdef.abcdef")) 2318 setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); 2319 2320 open(QIODevice::ReadOnly); 2321 QTimer::singleShot(0, this, SLOT(timeout())); 2322 } 2323 ~FakeReply() 2324 { 2325 close(); 2326 } 2327 virtual void abort() {} 2328 virtual void close() {} 2329 2330 protected: 2331 qint64 readData(char*, qint64) 2332 { 2333 return 0; 2334 } 2335 2336 private slots: 2337 void timeout() 2338 { 2339 if (request().url() == QUrl("qrc://test1.html")) 2340 emit error(this->error()); 2341 else if (request().url() == QUrl("http://abcdef.abcdef/")) 2342 emit metaDataChanged(); 2343 #ifndef QT_NO_OPENSSL 2344 else if (request().url() == QUrl("qrc:/fake-ssl-error.html")) 2345 return; 2346 #endif 2347 2348 emit readyRead(); 2349 emit finished(); 2350 } 2351 }; 2352 2353 class FakeNetworkManager : public QNetworkAccessManager { 2354 Q_OBJECT 2355 2356 public: 2357 FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } 2358 2359 protected: 2360 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) 2361 { 2362 QString url = request.url().toString(); 2363 if (op == QNetworkAccessManager::GetOperation) { 2364 if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/") 2365 return new FakeReply(request, this); 2366 #ifndef QT_NO_OPENSSL 2367 else if (url == "qrc:/fake-ssl-error.html") { 2368 FakeReply* reply = new FakeReply(request, this); 2369 QList<QSslError> errors; 2370 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); 2371 return reply; 2372 } 2373 #endif 2374 } 2375 2376 return QNetworkAccessManager::createRequest(op, request, outgoingData); 2377 } 2378 }; 2379 2380 void tst_QWebFrame::requestedUrl() 2381 { 2382 QWebPage page; 2383 QWebFrame* frame = page.mainFrame(); 2384 2385 // in few seconds, the image should be completely loaded 2386 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2387 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 2388 page.setNetworkAccessManager(networkManager); 2389 2390 frame->setUrl(QUrl("qrc:/test1.html")); 2391 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2392 QCOMPARE(spy.count(), 1); 2393 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html")); 2394 QCOMPARE(frame->url(), QUrl("qrc:/test2.html")); 2395 2396 frame->setUrl(QUrl("qrc:/non-existent.html")); 2397 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2398 QCOMPARE(spy.count(), 2); 2399 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html")); 2400 QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html")); 2401 2402 frame->setUrl(QUrl("http://abcdef.abcdef")); 2403 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2404 QCOMPARE(spy.count(), 3); 2405 QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/")); 2406 QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/")); 2407 2408 #ifndef QT_NO_OPENSSL 2409 qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); 2410 qRegisterMetaType<QNetworkReply* >("QNetworkReply*"); 2411 2412 QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); 2413 frame->setUrl(QUrl("qrc:/fake-ssl-error.html")); 2414 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2415 QCOMPARE(spy2.count(), 1); 2416 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html")); 2417 QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html")); 2418 #endif 2419 } 2420 2421 void tst_QWebFrame::requestedUrlAfterSetAndLoadFailures() 2422 { 2423 QWebPage page; 2424 QWebFrame* frame = page.mainFrame(); 2425 2426 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 2427 2428 const QUrl first("http://abcdef.abcdef/"); 2429 frame->setUrl(first); 2430 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 2431 QCOMPARE(frame->url(), first); 2432 QCOMPARE(frame->requestedUrl(), first); 2433 QVERIFY(!spy.at(0).first().toBool()); 2434 2435 const QUrl second("http://abcdef.abcdef/another_page.html"); 2436 QVERIFY(first != second); 2437 2438 frame->load(second); 2439 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 2440 QCOMPARE(frame->url(), first); 2441 QCOMPARE(frame->requestedUrl(), second); 2442 QVERIFY(!spy.at(1).first().toBool()); 2443 } 2444 2445 void tst_QWebFrame::javaScriptWindowObjectCleared_data() 2446 { 2447 QTest::addColumn<QString>("html"); 2448 QTest::addColumn<int>("signalCount"); 2449 QTest::newRow("with <script>") << "<html><body><script>i=0</script><p>hello world</p></body></html>" << 1; 2450 // NOTE: Empty scripts no longer cause this signal to be emitted. 2451 QTest::newRow("with empty <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 0; 2452 QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; 2453 } 2454 2455 void tst_QWebFrame::javaScriptWindowObjectCleared() 2456 { 2457 QWebPage page; 2458 QWebFrame* frame = page.mainFrame(); 2459 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 2460 QFETCH(QString, html); 2461 frame->setHtml(html); 2462 2463 QFETCH(int, signalCount); 2464 QCOMPARE(spy.count(), signalCount); 2465 } 2466 2467 void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate() 2468 { 2469 QWebPage page; 2470 QWebFrame* frame = page.mainFrame(); 2471 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 2472 frame->setHtml("<html></html>"); 2473 QCOMPARE(spy.count(), 0); 2474 frame->evaluateJavaScript("var a = 'a';"); 2475 QCOMPARE(spy.count(), 1); 2476 // no new clear for a new script: 2477 frame->evaluateJavaScript("var a = 1;"); 2478 QCOMPARE(spy.count(), 1); 2479 } 2480 2481 void tst_QWebFrame::setHtml() 2482 { 2483 QString html("<html><head></head><body><p>hello world</p></body></html>"); 2484 QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool))); 2485 m_view->page()->mainFrame()->setHtml(html); 2486 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); 2487 QCOMPARE(spy.count(), 1); 2488 } 2489 2490 void tst_QWebFrame::setHtmlWithResource() 2491 { 2492 QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>"); 2493 2494 QWebPage page; 2495 QWebFrame* frame = page.mainFrame(); 2496 2497 // in few seconds, the image should be completey loaded 2498 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2499 frame->setHtml(html); 2500 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2501 QCOMPARE(spy.count(), 1); 2502 2503 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 2504 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 2505 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 2506 2507 QString html2 = 2508 "<html>" 2509 "<head>" 2510 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" 2511 "</head>" 2512 "<body>" 2513 "<p id='idP'>some text</p>" 2514 "</body>" 2515 "</html>"; 2516 2517 // in few seconds, the CSS should be completey loaded 2518 frame->setHtml(html2); 2519 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2520 QCOMPARE(spy.size(), 2); 2521 2522 QWebElement p = frame->documentElement().findAll("p").at(0); 2523 QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red")); 2524 } 2525 2526 void tst_QWebFrame::setHtmlWithBaseURL() 2527 { 2528 if (!QDir(TESTS_SOURCE_DIR).exists()) 2529 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); 2530 2531 QDir::setCurrent(TESTS_SOURCE_DIR); 2532 2533 QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); 2534 2535 QWebPage page; 2536 QWebFrame* frame = page.mainFrame(); 2537 2538 // in few seconds, the image should be completey loaded 2539 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2540 2541 frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); 2542 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2543 QCOMPARE(spy.count(), 1); 2544 2545 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 2546 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 2547 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 2548 2549 // no history item has to be added. 2550 QCOMPARE(m_view->page()->history()->count(), 0); 2551 } 2552 2553 class MyPage : public QWebPage 2554 { 2555 public: 2556 MyPage() : QWebPage(), alerts(0) {} 2557 int alerts; 2558 2559 protected: 2560 virtual void javaScriptAlert(QWebFrame*, const QString& msg) 2561 { 2562 alerts++; 2563 QCOMPARE(msg, QString("foo")); 2564 // Should not be enough to trigger deferred loading, since we've upped the HTML 2565 // tokenizer delay in the Qt frameloader. See HTMLTokenizer::continueProcessing() 2566 QTest::qWait(1000); 2567 } 2568 }; 2569 2570 void tst_QWebFrame::setHtmlWithJSAlert() 2571 { 2572 QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>"); 2573 MyPage page; 2574 m_view->setPage(&page); 2575 page.mainFrame()->setHtml(html); 2576 QCOMPARE(page.alerts, 1); 2577 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); 2578 } 2579 2580 class TestNetworkManager : public QNetworkAccessManager 2581 { 2582 public: 2583 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} 2584 2585 QList<QUrl> requestedUrls; 2586 2587 protected: 2588 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { 2589 requestedUrls.append(request.url()); 2590 QNetworkRequest redirectedRequest = request; 2591 redirectedRequest.setUrl(QUrl("data:text/html,<p>hello")); 2592 return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData); 2593 } 2594 }; 2595 2596 void tst_QWebFrame::ipv6HostEncoding() 2597 { 2598 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 2599 m_page->setNetworkAccessManager(networkManager); 2600 networkManager->requestedUrls.clear(); 2601 2602 QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html"); 2603 m_view->setHtml("<p>Hi", baseUrl); 2604 m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();" 2605 "r.open('GET', 'http://[::1]/test.xml', false);" 2606 "r.send(null);" 2607 ); 2608 QCOMPARE(networkManager->requestedUrls.count(), 1); 2609 QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); 2610 } 2611 2612 void tst_QWebFrame::metaData() 2613 { 2614 m_view->setHtml("<html>" 2615 " <head>" 2616 " <meta name=\"description\" content=\"Test description\">" 2617 " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" 2618 " </head>" 2619 "</html>"); 2620 2621 QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData(); 2622 2623 QCOMPARE(metaData.count(), 2); 2624 2625 QCOMPARE(metaData.value("description"), QString("Test description")); 2626 QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); 2627 QCOMPARE(metaData.value("nonexistant"), QString()); 2628 2629 m_view->setHtml("<html>" 2630 " <head>" 2631 " <meta name=\"samekey\" content=\"FirstValue\">" 2632 " <meta name=\"samekey\" content=\"SecondValue\">" 2633 " </head>" 2634 "</html>"); 2635 2636 metaData = m_view->page()->mainFrame()->metaData(); 2637 2638 QCOMPARE(metaData.count(), 2); 2639 2640 QStringList values = metaData.values("samekey"); 2641 QCOMPARE(values.count(), 2); 2642 2643 QVERIFY(values.contains("FirstValue")); 2644 QVERIFY(values.contains("SecondValue")); 2645 2646 QCOMPARE(metaData.value("nonexistant"), QString()); 2647 } 2648 2649 #if !defined(Q_WS_MAEMO_5) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_COMBOBOX) 2650 void tst_QWebFrame::popupFocus() 2651 { 2652 QWebView view; 2653 view.setHtml("<html>" 2654 " <body>" 2655 " <select name=\"select\">" 2656 " <option>1</option>" 2657 " <option>2</option>" 2658 " </select>" 2659 " <input type=\"text\"> </input>" 2660 " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" 2661 "This test checks whether showing and hiding a popup" 2662 "takes the focus away from the webpage." 2663 " </textarea>" 2664 " </body>" 2665 "</html>"); 2666 view.resize(400, 100); 2667 // Call setFocus before show to work around http://bugreports.qt.nokia.com/browse/QTBUG-14762 2668 view.setFocus(); 2669 view.show(); 2670 QTest::qWaitForWindowShown(&view); 2671 view.activateWindow(); 2672 QTRY_VERIFY(view.hasFocus()); 2673 2674 // open the popup by clicking. check if focus is on the popup 2675 const QWebElement webCombo = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("select[name=select]")); 2676 QTest::mouseClick(&view, Qt::LeftButton, 0, webCombo.geometry().center()); 2677 QObject* webpopup = firstChildByClassName(&view, "QComboBox"); 2678 QComboBox* combo = qobject_cast<QComboBox*>(webpopup); 2679 QVERIFY(combo != 0); 2680 QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup 2681 2682 // hide the popup and check if focus is on the page 2683 combo->hidePopup(); 2684 QTRY_VERIFY(view.hasFocus()); // Focus should be back on the WebView 2685 } 2686 #endif 2687 2688 void tst_QWebFrame::inputFieldFocus() 2689 { 2690 QWebView view; 2691 view.setHtml("<html><body><input type=\"text\"></input></body></html>"); 2692 view.resize(400, 100); 2693 view.show(); 2694 QTest::qWaitForWindowShown(&view); 2695 view.activateWindow(); 2696 view.setFocus(); 2697 QTRY_VERIFY(view.hasFocus()); 2698 2699 // double the flashing time, should at least blink once already 2700 int delay = qApp->cursorFlashTime() * 2; 2701 2702 // focus the lineedit and check if it blinks 2703 bool autoSipEnabled = qApp->autoSipEnabled(); 2704 qApp->setAutoSipEnabled(false); 2705 const QWebElement inputElement = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("input[type=text]")); 2706 QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); 2707 m_inputFieldsTestView = &view; 2708 view.installEventFilter( this ); 2709 QTest::qWait(delay); 2710 QVERIFY2(m_inputFieldTestPaintCount >= 3, 2711 "The input field should have a blinking caret"); 2712 qApp->setAutoSipEnabled(autoSipEnabled); 2713 } 2714 2715 void tst_QWebFrame::hitTestContent() 2716 { 2717 QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); 2718 2719 QWebPage page; 2720 QWebFrame* frame = page.mainFrame(); 2721 frame->setHtml(html); 2722 page.setViewportSize(QSize(200, 0)); //no height so link is not visible 2723 const QWebElement linkElement = frame->documentElement().findFirst(QLatin1String("a#link")); 2724 QWebHitTestResult result = frame->hitTestContent(linkElement.geometry().center()); 2725 QCOMPARE(result.linkText(), QString("link text")); 2726 QWebElement link = result.linkElement(); 2727 QCOMPARE(link.attribute("target"), QString("_foo")); 2728 } 2729 2730 void tst_QWebFrame::jsByteArray() 2731 { 2732 QByteArray ba("hello world"); 2733 m_myObject->setByteArrayProperty(ba); 2734 2735 // read-only property 2736 QCOMPARE(m_myObject->byteArrayProperty(), ba); 2737 QString type; 2738 QVariant v = evalJSV("myObject.byteArrayProperty"); 2739 QCOMPARE(int(v.type()), int(QVariant::ByteArray)); 2740 2741 QCOMPARE(v.toByteArray(), ba); 2742 } 2743 2744 void tst_QWebFrame::ownership() 2745 { 2746 // test ownership 2747 { 2748 QPointer<QObject> ptr = new QObject(); 2749 QVERIFY(ptr != 0); 2750 { 2751 QWebPage page; 2752 QWebFrame* frame = page.mainFrame(); 2753 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership); 2754 } 2755 QVERIFY(ptr == 0); 2756 } 2757 { 2758 QPointer<QObject> ptr = new QObject(); 2759 QVERIFY(ptr != 0); 2760 QObject* before = ptr; 2761 { 2762 QWebPage page; 2763 QWebFrame* frame = page.mainFrame(); 2764 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership); 2765 } 2766 QVERIFY(ptr == before); 2767 delete ptr; 2768 } 2769 { 2770 QObject* parent = new QObject(); 2771 QObject* child = new QObject(parent); 2772 QWebPage page; 2773 QWebFrame* frame = page.mainFrame(); 2774 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership); 2775 QVariant v = frame->evaluateJavaScript("test"); 2776 QCOMPARE(qvariant_cast<QObject*>(v), child); 2777 delete parent; 2778 v = frame->evaluateJavaScript("test"); 2779 QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0); 2780 } 2781 { 2782 QPointer<QObject> ptr = new QObject(); 2783 QVERIFY(ptr != 0); 2784 { 2785 QWebPage page; 2786 QWebFrame* frame = page.mainFrame(); 2787 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership); 2788 } 2789 // no parent, so it should be like ScriptOwnership 2790 QVERIFY(ptr == 0); 2791 } 2792 { 2793 QObject* parent = new QObject(); 2794 QPointer<QObject> child = new QObject(parent); 2795 QVERIFY(child != 0); 2796 { 2797 QWebPage page; 2798 QWebFrame* frame = page.mainFrame(); 2799 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership); 2800 } 2801 // has parent, so it should be like QtOwnership 2802 QVERIFY(child != 0); 2803 delete parent; 2804 } 2805 } 2806 2807 void tst_QWebFrame::nullValue() 2808 { 2809 QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null"); 2810 QVERIFY(v.isNull()); 2811 } 2812 2813 void tst_QWebFrame::baseUrl_data() 2814 { 2815 QTest::addColumn<QString>("html"); 2816 QTest::addColumn<QUrl>("loadUrl"); 2817 QTest::addColumn<QUrl>("url"); 2818 QTest::addColumn<QUrl>("baseUrl"); 2819 2820 QTest::newRow("null") << QString() << QUrl() 2821 << QUrl("about:blank") << QUrl("about:blank"); 2822 2823 QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") 2824 << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); 2825 2826 QString html = "<html>" 2827 "<head>" 2828 "<base href=\"http://foobaz.bar/\" />" 2829 "</head>" 2830 "</html>"; 2831 QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") 2832 << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); 2833 } 2834 2835 void tst_QWebFrame::baseUrl() 2836 { 2837 QFETCH(QString, html); 2838 QFETCH(QUrl, loadUrl); 2839 QFETCH(QUrl, url); 2840 QFETCH(QUrl, baseUrl); 2841 2842 m_page->mainFrame()->setHtml(html, loadUrl); 2843 QCOMPARE(m_page->mainFrame()->url(), url); 2844 QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl); 2845 } 2846 2847 void tst_QWebFrame::hasSetFocus() 2848 { 2849 QString html("<html><body><p>top</p>" \ 2850 "<iframe width='80%' height='30%'/>" \ 2851 "</body></html>"); 2852 2853 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); 2854 m_page->mainFrame()->setHtml(html); 2855 2856 waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200); 2857 QCOMPARE(loadSpy.size(), 1); 2858 2859 QList<QWebFrame*> children = m_page->mainFrame()->childFrames(); 2860 QWebFrame* frame = children.at(0); 2861 QString innerHtml("<html><body><p>another iframe</p>" \ 2862 "<iframe width='80%' height='30%'/>" \ 2863 "</body></html>"); 2864 frame->setHtml(innerHtml); 2865 2866 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2867 QCOMPARE(loadSpy.size(), 2); 2868 2869 m_page->mainFrame()->setFocus(); 2870 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 2871 2872 for (int i = 0; i < children.size(); ++i) { 2873 children.at(i)->setFocus(); 2874 QTRY_VERIFY(children.at(i)->hasFocus()); 2875 QVERIFY(!m_page->mainFrame()->hasFocus()); 2876 } 2877 2878 m_page->mainFrame()->setFocus(); 2879 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 2880 } 2881 2882 void tst_QWebFrame::render() 2883 { 2884 QString html("<html>" \ 2885 "<head><style>" \ 2886 "body, iframe { margin: 0px; border: none; }" \ 2887 "</style></head>" \ 2888 "<body><iframe width='100px' height='100px'/></body>" \ 2889 "</html>"); 2890 2891 QWebPage page; 2892 page.mainFrame()->setHtml(html); 2893 2894 QList<QWebFrame*> frames = page.mainFrame()->childFrames(); 2895 QWebFrame *frame = frames.at(0); 2896 QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>"); 2897 frame->setHtml(innerHtml); 2898 2899 QPicture picture; 2900 2901 QSize size = page.mainFrame()->contentsSize(); 2902 page.setViewportSize(size); 2903 2904 // render contents layer only (the iframe is smaller than the image, so it will have scrollbars) 2905 QPainter painter1(&picture); 2906 frame->render(&painter1, QWebFrame::ContentsLayer); 2907 painter1.end(); 2908 2909 QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width()); 2910 QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height()); 2911 2912 // render everything, should be the size of the iframe 2913 QPainter painter2(&picture); 2914 frame->render(&painter2, QWebFrame::AllLayers); 2915 painter2.end(); 2916 2917 QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px 2918 QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px 2919 } 2920 2921 2922 class DummyPaintEngine: public QPaintEngine { 2923 public: 2924 2925 DummyPaintEngine() 2926 : QPaintEngine(QPaintEngine::AllFeatures) 2927 , renderHints(0) 2928 { 2929 } 2930 2931 bool begin(QPaintDevice*) 2932 { 2933 setActive(true); 2934 return true; 2935 } 2936 2937 bool end() 2938 { 2939 setActive(false); 2940 return false; 2941 } 2942 2943 void updateState(const QPaintEngineState& state) 2944 { 2945 renderHints = state.renderHints(); 2946 } 2947 2948 void drawPath(const QPainterPath&) { } 2949 void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } 2950 2951 QPaintEngine::Type type() const 2952 { 2953 return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); 2954 } 2955 2956 QPainter::RenderHints renderHints; 2957 }; 2958 2959 class DummyPaintDevice: public QPaintDevice { 2960 public: 2961 DummyPaintDevice() 2962 : QPaintDevice() 2963 , m_engine(new DummyPaintEngine) 2964 { 2965 } 2966 2967 ~DummyPaintDevice() 2968 { 2969 delete m_engine; 2970 } 2971 2972 QPaintEngine* paintEngine() const 2973 { 2974 return m_engine; 2975 } 2976 2977 QPainter::RenderHints renderHints() const 2978 { 2979 return m_engine->renderHints; 2980 } 2981 2982 protected: 2983 int metric(PaintDeviceMetric metric) const; 2984 2985 private: 2986 DummyPaintEngine* m_engine; 2987 friend class DummyPaintEngine; 2988 }; 2989 2990 2991 int DummyPaintDevice::metric(PaintDeviceMetric metric) const 2992 { 2993 switch (metric) { 2994 case PdmWidth: 2995 return 400; 2996 break; 2997 2998 case PdmHeight: 2999 return 200; 3000 break; 3001 3002 case PdmNumColors: 3003 return INT_MAX; 3004 break; 3005 3006 case PdmDepth: 3007 return 32; 3008 break; 3009 3010 default: 3011 break; 3012 } 3013 return 0; 3014 } 3015 3016 void tst_QWebFrame::renderHints() 3017 { 3018 QString html("<html><body><p>Hello, world!</p></body></html>"); 3019 3020 QWebPage page; 3021 page.mainFrame()->setHtml(html); 3022 page.setViewportSize(page.mainFrame()->contentsSize()); 3023 3024 // We will call frame->render and trap the paint engine state changes 3025 // to ensure that GraphicsContext does not clobber the render hints. 3026 DummyPaintDevice buffer; 3027 QPainter painter(&buffer); 3028 3029 painter.setRenderHint(QPainter::TextAntialiasing, false); 3030 page.mainFrame()->render(&painter); 3031 QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); 3032 QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); 3033 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 3034 3035 painter.setRenderHint(QPainter::TextAntialiasing, true); 3036 page.mainFrame()->render(&painter); 3037 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 3038 QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); 3039 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 3040 3041 painter.setRenderHint(QPainter::SmoothPixmapTransform, true); 3042 page.mainFrame()->render(&painter); 3043 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 3044 QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); 3045 QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); 3046 3047 painter.setRenderHint(QPainter::HighQualityAntialiasing, true); 3048 page.mainFrame()->render(&painter); 3049 QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); 3050 QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); 3051 QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); 3052 } 3053 3054 void tst_QWebFrame::scrollPosition() 3055 { 3056 // enlarged image in a small viewport, to provoke the scrollbars to appear 3057 QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); 3058 3059 QWebPage page; 3060 page.setViewportSize(QSize(200, 200)); 3061 3062 QWebFrame* frame = page.mainFrame(); 3063 frame->setHtml(html); 3064 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 3065 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 3066 3067 // try to set the scroll offset programmatically 3068 frame->setScrollPosition(QPoint(23, 29)); 3069 QCOMPARE(frame->scrollPosition().x(), 23); 3070 QCOMPARE(frame->scrollPosition().y(), 29); 3071 3072 int x = frame->evaluateJavaScript("window.scrollX").toInt(); 3073 int y = frame->evaluateJavaScript("window.scrollY").toInt(); 3074 QCOMPARE(x, 23); 3075 QCOMPARE(y, 29); 3076 } 3077 3078 void tst_QWebFrame::scrollToAnchor() 3079 { 3080 QWebPage page; 3081 page.setViewportSize(QSize(480, 800)); 3082 QWebFrame* frame = page.mainFrame(); 3083 3084 QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>" 3085 "<p><a id=\"foo\">This</a> is an anchor</p>" 3086 "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>" 3087 "</body></html>"); 3088 frame->setHtml(html); 3089 frame->setScrollPosition(QPoint(0, 0)); 3090 QCOMPARE(frame->scrollPosition().x(), 0); 3091 QCOMPARE(frame->scrollPosition().y(), 0); 3092 3093 QWebElement fooAnchor = frame->findFirstElement("a[id=foo]"); 3094 3095 frame->scrollToAnchor("foo"); 3096 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); 3097 3098 frame->scrollToAnchor("bar"); 3099 frame->scrollToAnchor("foo"); 3100 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); 3101 3102 frame->scrollToAnchor("top"); 3103 QCOMPARE(frame->scrollPosition().y(), 0); 3104 3105 frame->scrollToAnchor("bar"); 3106 frame->scrollToAnchor("notexist"); 3107 QVERIFY(frame->scrollPosition().y() != 0); 3108 } 3109 3110 3111 void tst_QWebFrame::scrollbarsOff() 3112 { 3113 QWebView view; 3114 QWebFrame* mainFrame = view.page()->mainFrame(); 3115 3116 mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 3117 mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 3118 3119 QString html("<script>" \ 3120 " function checkScrollbar() {" \ 3121 " if (innerWidth === document.documentElement.offsetWidth)" \ 3122 " document.getElementById('span1').innerText = 'SUCCESS';" \ 3123 " else" \ 3124 " document.getElementById('span1').innerText = 'FAIL';" \ 3125 " }" \ 3126 "</script>" \ 3127 "<body>" \ 3128 " <div style='margin-top:1000px ; margin-left:1000px'>" \ 3129 " <a id='offscreen' href='a'>End</a>" \ 3130 " </div>" \ 3131 "<span id='span1'></span>" \ 3132 "</body>"); 3133 3134 3135 view.setHtml(html); 3136 ::waitForSignal(&view, SIGNAL(loadFinished(bool))); 3137 3138 mainFrame->evaluateJavaScript("checkScrollbar();"); 3139 QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS")); 3140 } 3141 3142 void tst_QWebFrame::horizontalScrollAfterBack() 3143 { 3144 QWebView view; 3145 QWebFrame* frame = view.page()->mainFrame(); 3146 QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); 3147 3148 view.page()->settings()->setMaximumPagesInCache(2); 3149 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); 3150 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); 3151 3152 view.load(QUrl("qrc:/testiframe2.html")); 3153 view.resize(200, 200); 3154 QTRY_COMPARE(loadSpy.count(), 1); 3155 QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); 3156 3157 view.load(QUrl("qrc:/testiframe.html")); 3158 QTRY_COMPARE(loadSpy.count(), 2); 3159 3160 view.page()->triggerAction(QWebPage::Back); 3161 QTRY_COMPARE(loadSpy.count(), 3); 3162 QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); 3163 } 3164 3165 void tst_QWebFrame::evaluateWillCauseRepaint() 3166 { 3167 QWebView view; 3168 QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" 3169 "junk</div>bottom</body></html>"); 3170 view.setHtml(html); 3171 view.show(); 3172 3173 QTest::qWaitForWindowShown(&view); 3174 view.page()->mainFrame()->evaluateJavaScript( 3175 "document.getElementById('junk').style.display = 'none';"); 3176 3177 ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); 3178 } 3179 3180 class TestFactory : public QObject 3181 { 3182 Q_OBJECT 3183 public: 3184 TestFactory() 3185 : obj(0), counter(0) 3186 {} 3187 3188 Q_INVOKABLE QObject* getNewObject() 3189 { 3190 delete obj; 3191 obj = new QObject(this); 3192 obj->setObjectName(QLatin1String("test") + QString::number(++counter)); 3193 return obj; 3194 3195 } 3196 3197 QObject* obj; 3198 int counter; 3199 }; 3200 3201 void tst_QWebFrame::qObjectWrapperWithSameIdentity() 3202 { 3203 m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>" 3204 "<body><span id='span1'>test</span></body>"); 3205 3206 QWebFrame* mainFrame = m_view->page()->mainFrame(); 3207 QCOMPARE(mainFrame->toPlainText(), QString("test")); 3208 3209 mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership); 3210 3211 mainFrame->evaluateJavaScript("triggerBug();"); 3212 QCOMPARE(mainFrame->toPlainText(), QString("test1")); 3213 3214 mainFrame->evaluateJavaScript("triggerBug();"); 3215 QCOMPARE(mainFrame->toPlainText(), QString("test2")); 3216 } 3217 3218 void tst_QWebFrame::introspectQtMethods_data() 3219 { 3220 QTest::addColumn<QString>("objectExpression"); 3221 QTest::addColumn<QString>("methodName"); 3222 QTest::addColumn<QStringList>("expectedPropertyNames"); 3223 3224 QTest::newRow("myObject.mySignal") 3225 << "myObject" << "mySignal" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 3226 QTest::newRow("myObject.mySlot") 3227 << "myObject" << "mySlot" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 3228 QTest::newRow("myObject.myInvokable") 3229 << "myObject" << "myInvokable" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 3230 QTest::newRow("myObject.mySignal.connect") 3231 << "myObject.mySignal" << "connect" << (QStringList() << "length" << "name"); 3232 QTest::newRow("myObject.mySignal.disconnect") 3233 << "myObject.mySignal" << "disconnect" << (QStringList() << "length" << "name"); 3234 } 3235 3236 void tst_QWebFrame::introspectQtMethods() 3237 { 3238 QFETCH(QString, objectExpression); 3239 QFETCH(QString, methodName); 3240 QFETCH(QStringList, expectedPropertyNames); 3241 3242 QString methodLookup = QString::fromLatin1("%0['%1']").arg(objectExpression).arg(methodName); 3243 QCOMPARE(evalJSV(QString::fromLatin1("Object.getOwnPropertyNames(%0).sort()").arg(methodLookup)).toStringList(), expectedPropertyNames); 3244 3245 for (int i = 0; i < expectedPropertyNames.size(); ++i) { 3246 QString name = expectedPropertyNames.at(i); 3247 QCOMPARE(evalJS(QString::fromLatin1("%0.hasOwnProperty('%1')").arg(methodLookup).arg(name)), sTrue); 3248 evalJS(QString::fromLatin1("var descriptor = Object.getOwnPropertyDescriptor(%0, '%1')").arg(methodLookup).arg(name)); 3249 QCOMPARE(evalJS("typeof descriptor"), QString::fromLatin1("object")); 3250 QCOMPARE(evalJS("descriptor.get"), sUndefined); 3251 QCOMPARE(evalJS("descriptor.set"), sUndefined); 3252 QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue); 3253 QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse); 3254 QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse); 3255 } 3256 3257 QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty()); 3258 } 3259 3260 void tst_QWebFrame::setContent_data() 3261 { 3262 QTest::addColumn<QString>("mimeType"); 3263 QTest::addColumn<QByteArray>("testContents"); 3264 QTest::addColumn<QString>("expected"); 3265 3266 QString str = QString::fromUtf8(" "); 3267 QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str; 3268 3269 QTextCodec *utf16 = QTextCodec::codecForName("UTF-16"); 3270 if (utf16) 3271 QTest::newRow("UTF-16 plain text") << "text/plain; charset=utf-16" << utf16->fromUnicode(str) << str; 3272 3273 str = QString::fromUtf8("Une chane de caractres sa faon."); 3274 QTest::newRow("latin-1 plain text") << "text/plain; charset=iso-8859-1" << str.toLatin1() << str; 3275 3276 3277 } 3278 3279 void tst_QWebFrame::setContent() 3280 { 3281 QFETCH(QString, mimeType); 3282 QFETCH(QByteArray, testContents); 3283 QFETCH(QString, expected); 3284 m_view->setContent(testContents, mimeType); 3285 QWebFrame* mainFrame = m_view->page()->mainFrame(); 3286 QCOMPARE(expected , mainFrame->toPlainText()); 3287 } 3288 3289 class CacheNetworkAccessManager : public QNetworkAccessManager { 3290 public: 3291 CacheNetworkAccessManager(QObject* parent = 0) 3292 : QNetworkAccessManager(parent) 3293 , m_lastCacheLoad(QNetworkRequest::PreferNetwork) 3294 { 3295 } 3296 3297 virtual QNetworkReply* createRequest(Operation, const QNetworkRequest& request, QIODevice*) 3298 { 3299 QVariant cacheLoad = request.attribute(QNetworkRequest::CacheLoadControlAttribute); 3300 if (cacheLoad.isValid()) 3301 m_lastCacheLoad = static_cast<QNetworkRequest::CacheLoadControl>(cacheLoad.toUInt()); 3302 else 3303 m_lastCacheLoad = QNetworkRequest::PreferNetwork; // default value 3304 return new FakeReply(request, this); 3305 } 3306 3307 QNetworkRequest::CacheLoadControl lastCacheLoad() const 3308 { 3309 return m_lastCacheLoad; 3310 } 3311 3312 private: 3313 QNetworkRequest::CacheLoadControl m_lastCacheLoad; 3314 }; 3315 3316 void tst_QWebFrame::setCacheLoadControlAttribute() 3317 { 3318 QWebPage page; 3319 CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); 3320 page.setNetworkAccessManager(manager); 3321 QWebFrame* frame = page.mainFrame(); 3322 3323 QNetworkRequest request(QUrl("http://abcdef.abcdef/")); 3324 3325 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); 3326 frame->load(request); 3327 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); 3328 3329 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); 3330 frame->load(request); 3331 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); 3332 3333 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); 3334 frame->load(request); 3335 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); 3336 3337 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); 3338 frame->load(request); 3339 QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); 3340 } 3341 3342 void tst_QWebFrame::webElementSlotOnly() 3343 { 3344 MyWebElementSlotOnlyObject object; 3345 m_page->mainFrame()->setHtml("<html><head><body></body></html>"); 3346 m_page->mainFrame()->addToJavaScriptWindowObject("myWebElementSlotObject", &object); 3347 evalJS("myWebElementSlotObject.doSomethingWithWebElement(document.body)"); 3348 QCOMPARE(evalJS("myWebElementSlotObject.tagName"), QString("BODY")); 3349 } 3350 3351 void tst_QWebFrame::setUrlWithPendingLoads() 3352 { 3353 QWebPage page; 3354 page.mainFrame()->setHtml("<img src='dummy:'/>"); 3355 page.mainFrame()->setUrl(QUrl("about:blank")); 3356 } 3357 3358 void tst_QWebFrame::setUrlWithFragment_data() 3359 { 3360 QTest::addColumn<QUrl>("previousUrl"); 3361 QTest::newRow("empty") << QUrl(); 3362 QTest::newRow("same URL no fragment") << QUrl("qrc:/test1.html"); 3363 // See comments in setUrlSameUrl about using setUrl() with the same url(). 3364 QTest::newRow("same URL with same fragment") << QUrl("qrc:/test1.html#"); 3365 QTest::newRow("same URL with different fragment") << QUrl("qrc:/test1.html#anotherFragment"); 3366 QTest::newRow("another URL") << QUrl("qrc:/test2.html"); 3367 } 3368 3369 // Based on bug report https://bugs.webkit.org/show_bug.cgi?id=32723 3370 void tst_QWebFrame::setUrlWithFragment() 3371 { 3372 QFETCH(QUrl, previousUrl); 3373 3374 QWebPage page; 3375 QWebFrame* frame = page.mainFrame(); 3376 3377 if (!previousUrl.isEmpty()) { 3378 frame->load(previousUrl); 3379 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 3380 QCOMPARE(frame->url(), previousUrl); 3381 } 3382 3383 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 3384 const QUrl url("qrc:/test1.html#"); 3385 QVERIFY(!url.fragment().isNull()); 3386 3387 frame->setUrl(url); 3388 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 3389 3390 QCOMPARE(spy.count(), 1); 3391 QVERIFY(!frame->toPlainText().isEmpty()); 3392 QCOMPARE(frame->requestedUrl(), url); 3393 QCOMPARE(frame->url(), url); 3394 } 3395 3396 void tst_QWebFrame::setUrlToEmpty() 3397 { 3398 int expectedLoadFinishedCount = 0; 3399 const QUrl aboutBlank("about:blank"); 3400 const QUrl url("qrc:/test2.html"); 3401 3402 QWebPage page; 3403 QWebFrame* frame = page.mainFrame(); 3404 QCOMPARE(frame->url(), QUrl()); 3405 QCOMPARE(frame->requestedUrl(), QUrl()); 3406 QCOMPARE(frame->baseUrl(), QUrl()); 3407 3408 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 3409 3410 // Set existing url 3411 frame->setUrl(url); 3412 expectedLoadFinishedCount++; 3413 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 3414 3415 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3416 QCOMPARE(frame->url(), url); 3417 QCOMPARE(frame->requestedUrl(), url); 3418 QCOMPARE(frame->baseUrl(), url); 3419 3420 // Set empty url 3421 frame->setUrl(QUrl()); 3422 expectedLoadFinishedCount++; 3423 3424 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3425 QCOMPARE(frame->url(), aboutBlank); 3426 QCOMPARE(frame->requestedUrl(), QUrl()); 3427 QCOMPARE(frame->baseUrl(), aboutBlank); 3428 3429 // Set existing url 3430 frame->setUrl(url); 3431 expectedLoadFinishedCount++; 3432 ::waitForSignal(frame, SIGNAL(loadFinished(bool))); 3433 3434 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3435 QCOMPARE(frame->url(), url); 3436 QCOMPARE(frame->requestedUrl(), url); 3437 QCOMPARE(frame->baseUrl(), url); 3438 3439 // Load empty url 3440 frame->load(QUrl()); 3441 expectedLoadFinishedCount++; 3442 3443 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3444 QCOMPARE(frame->url(), aboutBlank); 3445 QCOMPARE(frame->requestedUrl(), QUrl()); 3446 QCOMPARE(frame->baseUrl(), aboutBlank); 3447 } 3448 3449 void tst_QWebFrame::setUrlToInvalid() 3450 { 3451 QWebPage page; 3452 QWebFrame* frame = page.mainFrame(); 3453 3454 const QUrl invalidUrl("http://strange;hostname/here"); 3455 QVERIFY(!invalidUrl.isEmpty()); 3456 QVERIFY(!invalidUrl.isValid()); 3457 QVERIFY(invalidUrl != QUrl()); 3458 3459 frame->setUrl(invalidUrl); 3460 QCOMPARE(frame->url(), invalidUrl); 3461 QCOMPARE(frame->requestedUrl(), invalidUrl); 3462 QCOMPARE(frame->baseUrl(), invalidUrl); 3463 3464 // QUrls equivalent to QUrl() will be treated as such. 3465 const QUrl aboutBlank("about:blank"); 3466 const QUrl anotherInvalidUrl("1http://bugs.webkit.org"); 3467 QVERIFY(!anotherInvalidUrl.isEmpty()); // and they are not necessarily empty. 3468 QVERIFY(!anotherInvalidUrl.isValid()); 3469 QCOMPARE(anotherInvalidUrl, QUrl()); 3470 3471 frame->setUrl(anotherInvalidUrl); 3472 QCOMPARE(frame->url(), aboutBlank); 3473 QCOMPARE(frame->requestedUrl(), anotherInvalidUrl); 3474 QCOMPARE(frame->baseUrl(), aboutBlank); 3475 } 3476 3477 void tst_QWebFrame::setUrlHistory() 3478 { 3479 const QUrl aboutBlank("about:blank"); 3480 QUrl url; 3481 int expectedLoadFinishedCount = 0; 3482 QWebFrame* frame = m_page->mainFrame(); 3483 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 3484 3485 QCOMPARE(m_page->history()->count(), 0); 3486 3487 frame->setUrl(QUrl()); 3488 expectedLoadFinishedCount++; 3489 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3490 QCOMPARE(frame->url(), aboutBlank); 3491 QCOMPARE(frame->requestedUrl(), QUrl()); 3492 QCOMPARE(m_page->history()->count(), 0); 3493 3494 url = QUrl("http://non.existant/"); 3495 frame->setUrl(url); 3496 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 3497 expectedLoadFinishedCount++; 3498 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3499 QCOMPARE(frame->url(), url); 3500 QCOMPARE(frame->requestedUrl(), url); 3501 QCOMPARE(m_page->history()->count(), 0); 3502 3503 url = QUrl("qrc:/test1.html"); 3504 frame->setUrl(url); 3505 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 3506 expectedLoadFinishedCount++; 3507 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3508 QCOMPARE(frame->url(), url); 3509 QCOMPARE(frame->requestedUrl(), url); 3510 QCOMPARE(m_page->history()->count(), 1); 3511 3512 frame->setUrl(QUrl()); 3513 expectedLoadFinishedCount++; 3514 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3515 QCOMPARE(frame->url(), aboutBlank); 3516 QCOMPARE(frame->requestedUrl(), QUrl()); 3517 QCOMPARE(m_page->history()->count(), 1); 3518 3519 // Loading same page as current in history, so history count doesn't change. 3520 url = QUrl("qrc:/test1.html"); 3521 frame->setUrl(url); 3522 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 3523 expectedLoadFinishedCount++; 3524 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3525 QCOMPARE(frame->url(), url); 3526 QCOMPARE(frame->requestedUrl(), url); 3527 QCOMPARE(m_page->history()->count(), 1); 3528 3529 url = QUrl("qrc:/test2.html"); 3530 frame->setUrl(url); 3531 ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); 3532 expectedLoadFinishedCount++; 3533 QCOMPARE(spy.count(), expectedLoadFinishedCount); 3534 QCOMPARE(frame->url(), url); 3535 QCOMPARE(frame->requestedUrl(), url); 3536 QCOMPARE(m_page->history()->count(), 2); 3537 } 3538 3539 void tst_QWebFrame::setUrlSameUrl() 3540 { 3541 const QUrl url1("qrc:/test1.html"); 3542 const QUrl url2("qrc:/test2.html"); 3543 3544 QWebPage page; 3545 QWebFrame* frame = page.mainFrame(); 3546 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 3547 page.setNetworkAccessManager(networkManager); 3548 3549 QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); 3550 3551 frame->setUrl(url1); 3552 waitForSignal(frame, SIGNAL(loadFinished(bool))); 3553 QVERIFY(frame->url() != url1); // Nota bene: our QNAM redirects url1 to url2 3554 QCOMPARE(frame->url(), url2); 3555 QCOMPARE(spy.count(), 1); 3556 3557 frame->setUrl(url1); 3558 waitForSignal(frame, SIGNAL(loadFinished(bool))); 3559 QVERIFY(frame->url() != url1); 3560 QCOMPARE(frame->url(), url2); 3561 QCOMPARE(spy.count(), 2); 3562 3563 // Now a case without redirect. The existing behavior we have for setUrl() 3564 // is more like a "clear(); load()", so the page will be loaded again, even 3565 // if urlToBeLoaded == url(). This test should be changed if we want to 3566 // make setUrl() early return in this case. 3567 frame->setUrl(url2); 3568 waitForSignal(frame, SIGNAL(loadFinished(bool))); 3569 QCOMPARE(frame->url(), url2); 3570 QCOMPARE(spy.count(), 3); 3571 3572 frame->setUrl(url1); 3573 waitForSignal(frame, SIGNAL(loadFinished(bool))); 3574 QCOMPARE(frame->url(), url2); 3575 QCOMPARE(spy.count(), 4); 3576 } 3577 3578 static inline QUrl extractBaseUrl(const QUrl& url) 3579 { 3580 return url.resolved(QUrl()); 3581 } 3582 3583 void tst_QWebFrame::setUrlThenLoads_data() 3584 { 3585 QTest::addColumn<QUrl>("url"); 3586 QTest::addColumn<QUrl>("baseUrl"); 3587 3588 QTest::newRow("resource file") << QUrl("qrc:/test1.html") << extractBaseUrl(QUrl("qrc:/test1.html")); 3589 QTest::newRow("base specified in HTML") << QUrl("data:text/html,<head><base href=\"http://different.base/\"></head>") << QUrl("http://different.base/"); 3590 } 3591 3592 void tst_QWebFrame::setUrlThenLoads() 3593 { 3594 QFETCH(QUrl, url); 3595 QFETCH(QUrl, baseUrl); 3596 QWebFrame* frame = m_page->mainFrame(); 3597 QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl))); 3598 QSignalSpy startedSpy(frame, SIGNAL(loadStarted())); 3599 QSignalSpy finishedSpy(frame, SIGNAL(loadFinished(bool))); 3600 3601 frame->setUrl(url); 3602 QCOMPARE(startedSpy.count(), 1); 3603 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 3604 QCOMPARE(urlChangedSpy.count(), 1); 3605 QVERIFY(finishedSpy.at(0).first().toBool()); 3606 QCOMPARE(frame->url(), url); 3607 QCOMPARE(frame->requestedUrl(), url); 3608 QCOMPARE(frame->baseUrl(), baseUrl); 3609 3610 const QUrl urlToLoad1("qrc:/test2.html"); 3611 const QUrl urlToLoad2("qrc:/test1.html"); 3612 3613 // Just after first load. URL didn't changed yet. 3614 frame->load(urlToLoad1); 3615 QCOMPARE(startedSpy.count(), 2); 3616 QCOMPARE(frame->url(), url); 3617 QCOMPARE(frame->requestedUrl(), urlToLoad1); 3618 QCOMPARE(frame->baseUrl(), baseUrl); 3619 3620 // After first URL changed. 3621 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 3622 QCOMPARE(urlChangedSpy.count(), 2); 3623 QVERIFY(finishedSpy.at(1).first().toBool()); 3624 QCOMPARE(frame->url(), urlToLoad1); 3625 QCOMPARE(frame->requestedUrl(), urlToLoad1); 3626 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); 3627 3628 // Just after second load. URL didn't changed yet. 3629 frame->load(urlToLoad2); 3630 QCOMPARE(startedSpy.count(), 3); 3631 QCOMPARE(frame->url(), urlToLoad1); 3632 QCOMPARE(frame->requestedUrl(), urlToLoad2); 3633 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); 3634 3635 // After second URL changed. 3636 ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); 3637 QCOMPARE(urlChangedSpy.count(), 3); 3638 QVERIFY(finishedSpy.at(2).first().toBool()); 3639 QCOMPARE(frame->url(), urlToLoad2); 3640 QCOMPARE(frame->requestedUrl(), urlToLoad2); 3641 QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad2)); 3642 } 3643 3644 QTEST_MAIN(tst_QWebFrame) 3645 #include "tst_qwebframe.moc" 3646