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 <QPicture> 33 #include <QRegExp> 34 #include <QNetworkRequest> 35 #include <QNetworkReply> 36 #ifndef QT_NO_OPENSSL 37 #include <qsslerror.h> 38 #endif 39 #include "../util.h" 40 41 struct CustomType { 42 QString string; 43 }; 44 Q_DECLARE_METATYPE(CustomType) 45 46 Q_DECLARE_METATYPE(QBrush*) 47 Q_DECLARE_METATYPE(QObjectList) 48 Q_DECLARE_METATYPE(QList<int>) 49 Q_DECLARE_METATYPE(Qt::BrushStyle) 50 Q_DECLARE_METATYPE(QVariantList) 51 Q_DECLARE_METATYPE(QVariantMap) 52 53 class MyQObject : public QObject 54 { 55 Q_OBJECT 56 57 Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty) 58 Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty) 59 Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty) 60 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) 61 Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty) 62 Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty) 63 Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty) 64 Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false) 65 Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty) 66 Q_PROPERTY(int readOnlyProperty READ readOnlyProperty) 67 Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) 68 Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType) 69 Q_ENUMS(Policy Strategy) 70 Q_FLAGS(Ability) 71 72 public: 73 enum Policy { 74 FooPolicy = 0, 75 BarPolicy, 76 BazPolicy 77 }; 78 79 enum Strategy { 80 FooStrategy = 10, 81 BarStrategy, 82 BazStrategy 83 }; 84 85 enum AbilityFlag { 86 NoAbility = 0x000, 87 FooAbility = 0x001, 88 BarAbility = 0x080, 89 BazAbility = 0x200, 90 AllAbility = FooAbility | BarAbility | BazAbility 91 }; 92 93 Q_DECLARE_FLAGS(Ability, AbilityFlag) 94 95 MyQObject(QObject* parent = 0) 96 : QObject(parent), 97 m_intValue(123), 98 m_variantValue(QLatin1String("foo")), 99 m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))), 100 m_stringValue(QLatin1String("bar")), 101 m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")), 102 m_brushValue(QColor(10, 20, 30, 40)), 103 m_hiddenValue(456.0), 104 m_writeOnlyValue(789), 105 m_readOnlyValue(987), 106 m_qtFunctionInvoked(-1) { } 107 108 ~MyQObject() { } 109 110 int intProperty() const { 111 return m_intValue; 112 } 113 void setIntProperty(int value) { 114 m_intValue = value; 115 } 116 117 QVariant variantProperty() const { 118 return m_variantValue; 119 } 120 void setVariantProperty(const QVariant &value) { 121 m_variantValue = value; 122 } 123 124 QVariantList variantListProperty() const { 125 return m_variantListValue; 126 } 127 void setVariantListProperty(const QVariantList &value) { 128 m_variantListValue = value; 129 } 130 131 QString stringProperty() const { 132 return m_stringValue; 133 } 134 void setStringProperty(const QString &value) { 135 m_stringValue = value; 136 } 137 138 QStringList stringListProperty() const { 139 return m_stringListValue; 140 } 141 void setStringListProperty(const QStringList &value) { 142 m_stringListValue = value; 143 } 144 145 QByteArray byteArrayProperty() const { 146 return m_byteArrayValue; 147 } 148 void setByteArrayProperty(const QByteArray &value) { 149 m_byteArrayValue = value; 150 } 151 152 QBrush brushProperty() const { 153 return m_brushValue; 154 } 155 Q_INVOKABLE void setBrushProperty(const QBrush &value) { 156 m_brushValue = value; 157 } 158 159 double hiddenProperty() const { 160 return m_hiddenValue; 161 } 162 void setHiddenProperty(double value) { 163 m_hiddenValue = value; 164 } 165 166 int writeOnlyProperty() const { 167 return m_writeOnlyValue; 168 } 169 void setWriteOnlyProperty(int value) { 170 m_writeOnlyValue = value; 171 } 172 173 int readOnlyProperty() const { 174 return m_readOnlyValue; 175 } 176 177 QKeySequence shortcut() const { 178 return m_shortcut; 179 } 180 void setShortcut(const QKeySequence &seq) { 181 m_shortcut = seq; 182 } 183 184 CustomType propWithCustomType() const { 185 return m_customType; 186 } 187 void setPropWithCustomType(const CustomType &c) { 188 m_customType = c; 189 } 190 191 int qtFunctionInvoked() const { 192 return m_qtFunctionInvoked; 193 } 194 195 QVariantList qtFunctionActuals() const { 196 return m_actuals; 197 } 198 199 void resetQtFunctionInvoked() { 200 m_qtFunctionInvoked = -1; 201 m_actuals.clear(); 202 } 203 204 Q_INVOKABLE void myInvokable() { 205 m_qtFunctionInvoked = 0; 206 } 207 Q_INVOKABLE void myInvokableWithIntArg(int arg) { 208 m_qtFunctionInvoked = 1; 209 m_actuals << arg; 210 } 211 Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) { 212 m_qtFunctionInvoked = 2; 213 m_actuals << arg; 214 } 215 Q_INVOKABLE void myInvokableWithFloatArg(float arg) { 216 m_qtFunctionInvoked = 3; 217 m_actuals << arg; 218 } 219 Q_INVOKABLE void myInvokableWithDoubleArg(double arg) { 220 m_qtFunctionInvoked = 4; 221 m_actuals << arg; 222 } 223 Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) { 224 m_qtFunctionInvoked = 5; 225 m_actuals << arg; 226 } 227 Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) { 228 m_qtFunctionInvoked = 6; 229 m_actuals << arg1 << arg2; 230 } 231 Q_INVOKABLE int myInvokableReturningInt() { 232 m_qtFunctionInvoked = 7; 233 return 123; 234 } 235 Q_INVOKABLE qlonglong myInvokableReturningLongLong() { 236 m_qtFunctionInvoked = 39; 237 return 456; 238 } 239 Q_INVOKABLE QString myInvokableReturningString() { 240 m_qtFunctionInvoked = 8; 241 return QLatin1String("ciao"); 242 } 243 Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload 244 m_qtFunctionInvoked = 9; 245 m_actuals << arg1 << arg2; 246 } 247 Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) { 248 m_qtFunctionInvoked = 10; 249 m_actuals << policy; 250 } 251 Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) { 252 m_qtFunctionInvoked = 36; 253 m_actuals << policy; 254 } 255 Q_INVOKABLE Policy myInvokableReturningEnum() { 256 m_qtFunctionInvoked = 37; 257 return BazPolicy; 258 } 259 Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() { 260 m_qtFunctionInvoked = 38; 261 return BazPolicy; 262 } 263 Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() { 264 m_qtFunctionInvoked = 11; 265 return QVector<int>(); 266 } 267 Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) { 268 m_qtFunctionInvoked = 12; 269 } 270 Q_INVOKABLE QObject* myInvokableReturningQObjectStar() { 271 m_qtFunctionInvoked = 13; 272 return this; 273 } 274 Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) { 275 m_qtFunctionInvoked = 14; 276 m_actuals << qVariantFromValue(lst); 277 return lst; 278 } 279 Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) { 280 m_qtFunctionInvoked = 15; 281 m_actuals << v; 282 return v; 283 } 284 Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) { 285 m_qtFunctionInvoked = 16; 286 m_actuals << vm; 287 return vm; 288 } 289 Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) { 290 m_qtFunctionInvoked = 17; 291 m_actuals << qVariantFromValue(lst); 292 return lst; 293 } 294 Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) { 295 m_qtFunctionInvoked = 18; 296 m_actuals << qVariantFromValue(obj); 297 return obj; 298 } 299 Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) { 300 m_qtFunctionInvoked = 19; 301 m_actuals << qVariantFromValue(brush); 302 return brush; 303 } 304 Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) { 305 m_qtFunctionInvoked = 43; 306 m_actuals << qVariantFromValue(style); 307 } 308 Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) { 309 m_qtFunctionInvoked = 44; 310 m_actuals << qVariantFromValue(arg); 311 } 312 Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) { 313 m_qtFunctionInvoked = 45; 314 m_actuals << qVariantFromValue(arg); 315 } 316 Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) { 317 m_qtFunctionInvoked = 46; 318 m_actuals << qVariantFromValue(arg); 319 } 320 Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") { 321 m_qtFunctionInvoked = 47; 322 m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2); 323 } 324 Q_INVOKABLE QObject& myInvokableReturningRef() { 325 m_qtFunctionInvoked = 48; 326 return *this; 327 } 328 Q_INVOKABLE const QObject& myInvokableReturningConstRef() const { 329 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49; 330 return *this; 331 } 332 Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) { 333 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50; 334 m_actuals << qVariantFromValue(arg); 335 } 336 Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) { 337 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51; 338 m_actuals << qVariantFromValue(arg); 339 } 340 Q_INVOKABLE void myInvokableWithBoolArg(bool arg) { 341 m_qtFunctionInvoked = 52; 342 m_actuals << arg; 343 } 344 345 void emitMySignal() { 346 emit mySignal(); 347 } 348 void emitMySignalWithIntArg(int arg) { 349 emit mySignalWithIntArg(arg); 350 } 351 void emitMySignal2(bool arg) { 352 emit mySignal2(arg); 353 } 354 void emitMySignal2() { 355 emit mySignal2(); 356 } 357 void emitMySignalWithDateTimeArg(QDateTime dt) { 358 emit mySignalWithDateTimeArg(dt); 359 } 360 void emitMySignalWithRegexArg(QRegExp r) { 361 emit mySignalWithRegexArg(r); 362 } 363 364 public Q_SLOTS: 365 void mySlot() { 366 m_qtFunctionInvoked = 20; 367 } 368 void mySlotWithIntArg(int arg) { 369 m_qtFunctionInvoked = 21; 370 m_actuals << arg; 371 } 372 void mySlotWithDoubleArg(double arg) { 373 m_qtFunctionInvoked = 22; 374 m_actuals << arg; 375 } 376 void mySlotWithStringArg(const QString &arg) { 377 m_qtFunctionInvoked = 23; 378 m_actuals << arg; 379 } 380 381 void myOverloadedSlot() { 382 m_qtFunctionInvoked = 24; 383 } 384 void myOverloadedSlot(QObject* arg) { 385 m_qtFunctionInvoked = 41; 386 m_actuals << qVariantFromValue(arg); 387 } 388 void myOverloadedSlot(bool arg) { 389 m_qtFunctionInvoked = 25; 390 m_actuals << arg; 391 } 392 void myOverloadedSlot(const QStringList &arg) { 393 m_qtFunctionInvoked = 42; 394 m_actuals << arg; 395 } 396 void myOverloadedSlot(double arg) { 397 m_qtFunctionInvoked = 26; 398 m_actuals << arg; 399 } 400 void myOverloadedSlot(float arg) { 401 m_qtFunctionInvoked = 27; 402 m_actuals << arg; 403 } 404 void myOverloadedSlot(int arg) { 405 m_qtFunctionInvoked = 28; 406 m_actuals << arg; 407 } 408 void myOverloadedSlot(const QString &arg) { 409 m_qtFunctionInvoked = 29; 410 m_actuals << arg; 411 } 412 void myOverloadedSlot(const QColor &arg) { 413 m_qtFunctionInvoked = 30; 414 m_actuals << arg; 415 } 416 void myOverloadedSlot(const QBrush &arg) { 417 m_qtFunctionInvoked = 31; 418 m_actuals << arg; 419 } 420 void myOverloadedSlot(const QDateTime &arg) { 421 m_qtFunctionInvoked = 32; 422 m_actuals << arg; 423 } 424 void myOverloadedSlot(const QDate &arg) { 425 m_qtFunctionInvoked = 33; 426 m_actuals << arg; 427 } 428 void myOverloadedSlot(const QRegExp &arg) { 429 m_qtFunctionInvoked = 34; 430 m_actuals << arg; 431 } 432 void myOverloadedSlot(const QVariant &arg) { 433 m_qtFunctionInvoked = 35; 434 m_actuals << arg; 435 } 436 437 void qscript_call(int arg) { 438 m_qtFunctionInvoked = 40; 439 m_actuals << arg; 440 } 441 442 protected Q_SLOTS: 443 void myProtectedSlot() { 444 m_qtFunctionInvoked = 36; 445 } 446 447 private Q_SLOTS: 448 void myPrivateSlot() { } 449 450 Q_SIGNALS: 451 void mySignal(); 452 void mySignalWithIntArg(int arg); 453 void mySignalWithDoubleArg(double arg); 454 void mySignal2(bool arg = false); 455 void mySignalWithDateTimeArg(QDateTime dt); 456 void mySignalWithRegexArg(QRegExp r); 457 458 private: 459 int m_intValue; 460 QVariant m_variantValue; 461 QVariantList m_variantListValue; 462 QString m_stringValue; 463 QStringList m_stringListValue; 464 QByteArray m_byteArrayValue; 465 QBrush m_brushValue; 466 double m_hiddenValue; 467 int m_writeOnlyValue; 468 int m_readOnlyValue; 469 QKeySequence m_shortcut; 470 CustomType m_customType; 471 int m_qtFunctionInvoked; 472 QVariantList m_actuals; 473 }; 474 475 class MyOtherQObject : public MyQObject 476 { 477 public: 478 MyOtherQObject(QObject* parent = 0) 479 : MyQObject(parent) { } 480 }; 481 482 class MyEnumTestQObject : public QObject 483 { 484 Q_OBJECT 485 Q_PROPERTY(QString p1 READ p1) 486 Q_PROPERTY(QString p2 READ p2) 487 Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false) 488 Q_PROPERTY(QString p4 READ p4) 489 Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false) 490 Q_PROPERTY(QString p6 READ p6) 491 public: 492 MyEnumTestQObject(QObject* parent = 0) 493 : QObject(parent) { } 494 QString p1() const { 495 return QLatin1String("p1"); 496 } 497 QString p2() const { 498 return QLatin1String("p2"); 499 } 500 QString p3() const { 501 return QLatin1String("p3"); 502 } 503 QString p4() const { 504 return QLatin1String("p4"); 505 } 506 QString p5() const { 507 return QLatin1String("p5"); 508 } 509 QString p6() const { 510 return QLatin1String("p5"); 511 } 512 public Q_SLOTS: 513 void mySlot() { } 514 void myOtherSlot() { } 515 Q_SIGNALS: 516 void mySignal(); 517 }; 518 519 class tst_QWebFrame : public QObject 520 { 521 Q_OBJECT 522 523 public: 524 tst_QWebFrame(); 525 virtual ~tst_QWebFrame(); 526 bool eventFilter(QObject* watched, QEvent* event); 527 528 public slots: 529 void init(); 530 void cleanup(); 531 532 private slots: 533 void getSetStaticProperty(); 534 void getSetDynamicProperty(); 535 void getSetChildren(); 536 void callQtInvokable(); 537 void connectAndDisconnect(); 538 void classEnums(); 539 void classConstructor(); 540 void overrideInvokable(); 541 void transferInvokable(); 542 void findChild(); 543 void findChildren(); 544 void overloadedSlots(); 545 void enumerate_data(); 546 void enumerate(); 547 void objectDeleted(); 548 void typeConversion(); 549 void arrayObjectEnumerable(); 550 void symmetricUrl(); 551 void progressSignal(); 552 void urlChange(); 553 void domCycles(); 554 void requestedUrl(); 555 void javaScriptWindowObjectCleared_data(); 556 void javaScriptWindowObjectCleared(); 557 void javaScriptWindowObjectClearedOnEvaluate(); 558 void setHtml(); 559 void setHtmlWithResource(); 560 void setHtmlWithBaseURL(); 561 void ipv6HostEncoding(); 562 void metaData(); 563 void popupFocus(); 564 void hitTestContent(); 565 void jsByteArray(); 566 void ownership(); 567 void nullValue(); 568 void baseUrl_data(); 569 void baseUrl(); 570 void hasSetFocus(); 571 void render(); 572 void scrollPosition(); 573 void evaluateWillCauseRepaint(); 574 void qObjectWrapperWithSameIdentity(); 575 void scrollRecursively(); 576 void introspectQtMethods_data(); 577 void introspectQtMethods(); 578 579 private: 580 QString evalJS(const QString&s) { 581 // Convert an undefined return variant to the string "undefined" 582 QVariant ret = evalJSV(s); 583 if (ret.userType() == QMetaType::Void) 584 return "undefined"; 585 else 586 return ret.toString(); 587 } 588 QVariant evalJSV(const QString &s) { 589 return m_page->mainFrame()->evaluateJavaScript(s); 590 } 591 592 QString evalJS(const QString&s, QString& type) { 593 return evalJSV(s, type).toString(); 594 } 595 QVariant evalJSV(const QString &s, QString& type) { 596 // As a special measure, if we get an exception we set the type to 'error' 597 // (in ecma, an Error object has typeof object, but qtscript has a convenience function) 598 // Similarly, an array is an object, but we'd prefer to have a type of 'array' 599 // Also, consider a QMetaType::Void QVariant to be undefined 600 QString escaped = s; 601 escaped.replace('\'', "\\'"); // Don't preescape your single quotes! 602 evalJS("var retvalue;\ 603 var typevalue; \ 604 try {\ 605 retvalue = eval('" + escaped + "'); \ 606 typevalue = typeof retvalue; \ 607 if (retvalue instanceof Array) \ 608 typevalue = 'array'; \ 609 } \ 610 catch(e) {\ 611 retvalue = e.name + ': ' + e.message;\ 612 typevalue = 'error';\ 613 }"); 614 QVariant ret = evalJSV("retvalue"); 615 if (ret.userType() != QMetaType::Void) 616 type = evalJS("typevalue"); 617 else { 618 ret = QString("undefined"); 619 type = sUndefined; 620 } 621 evalJS("delete retvalue; delete typevalue"); 622 return ret; 623 } 624 QObject* firstChildByClassName(QObject* parent, const char* className) { 625 const QObjectList & children = parent->children(); 626 foreach (QObject* child, children) { 627 if (!strcmp(child->metaObject()->className(), className)) { 628 return child; 629 } 630 } 631 return 0; 632 } 633 634 const QString sTrue; 635 const QString sFalse; 636 const QString sUndefined; 637 const QString sArray; 638 const QString sFunction; 639 const QString sError; 640 const QString sString; 641 const QString sObject; 642 const QString sNumber; 643 644 private: 645 QWebView* m_view; 646 QWebPage* m_page; 647 MyQObject* m_myObject; 648 QWebView* m_popupTestView; 649 int m_popupTestPaintCount; 650 }; 651 652 tst_QWebFrame::tst_QWebFrame() 653 : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"), 654 sString("string"), sObject("object"), sNumber("number"), m_popupTestView(0), m_popupTestPaintCount(0) 655 { 656 } 657 658 tst_QWebFrame::~tst_QWebFrame() 659 { 660 } 661 662 bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event) 663 { 664 // used on the popupFocus test 665 if (watched == m_popupTestView) { 666 if (event->type() == QEvent::Paint) 667 m_popupTestPaintCount++; 668 } 669 return QObject::eventFilter(watched, event); 670 } 671 672 void tst_QWebFrame::init() 673 { 674 m_view = new QWebView(); 675 m_page = m_view->page(); 676 m_myObject = new MyQObject(); 677 m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject); 678 } 679 680 void tst_QWebFrame::cleanup() 681 { 682 delete m_view; 683 delete m_myObject; 684 } 685 686 void tst_QWebFrame::getSetStaticProperty() 687 { 688 QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined); 689 690 // initial value (set in MyQObject constructor) 691 { 692 QString type; 693 QVariant ret = evalJSV("myObject.intProperty", type); 694 QCOMPARE(type, sNumber); 695 QCOMPARE(ret.type(), QVariant::Double); 696 QCOMPARE(ret.toInt(), 123); 697 } 698 QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue); 699 700 { 701 QString type; 702 QVariant ret = evalJSV("myObject.variantProperty", type); 703 QCOMPARE(type, sString); 704 QCOMPARE(ret.type(), QVariant::String); 705 QCOMPARE(ret.toString(), QLatin1String("foo")); 706 } 707 QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue); 708 709 { 710 QString type; 711 QVariant ret = evalJSV("myObject.stringProperty", type); 712 QCOMPARE(type, sString); 713 QCOMPARE(ret.type(), QVariant::String); 714 QCOMPARE(ret.toString(), QLatin1String("bar")); 715 } 716 QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue); 717 718 { 719 QString type; 720 QVariant ret = evalJSV("myObject.variantListProperty", type); 721 QCOMPARE(type, sArray); 722 QCOMPARE(ret.type(), QVariant::List); 723 QVariantList vl = ret.value<QVariantList>(); 724 QCOMPARE(vl.size(), 2); 725 QCOMPARE(vl.at(0).toInt(), 123); 726 QCOMPARE(vl.at(1).toString(), QLatin1String("foo")); 727 } 728 QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue); 729 QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue); 730 QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue); 731 732 { 733 QString type; 734 QVariant ret = evalJSV("myObject.stringListProperty", type); 735 QCOMPARE(type, sArray); 736 QCOMPARE(ret.type(), QVariant::List); 737 QVariantList vl = ret.value<QVariantList>(); 738 QCOMPARE(vl.size(), 2); 739 QCOMPARE(vl.at(0).toString(), QLatin1String("zig")); 740 QCOMPARE(vl.at(1).toString(), QLatin1String("zag")); 741 } 742 QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue); 743 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); 744 QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig")); 745 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); 746 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag")); 747 748 // property change in C++ should be reflected in script 749 m_myObject->setIntProperty(456); 750 QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue); 751 m_myObject->setIntProperty(789); 752 QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue); 753 754 m_myObject->setVariantProperty(QLatin1String("bar")); 755 QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue); 756 m_myObject->setVariantProperty(42); 757 QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue); 758 m_myObject->setVariantProperty(qVariantFromValue(QBrush())); 759 //XFAIL 760 // QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant); 761 762 m_myObject->setStringProperty(QLatin1String("baz")); 763 QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue); 764 m_myObject->setStringProperty(QLatin1String("zab")); 765 QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue); 766 767 // property change in script should be reflected in C++ 768 QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123")); 769 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); 770 QCOMPARE(m_myObject->intProperty(), 123); 771 QCOMPARE(evalJS("myObject.intProperty = 'ciao!';" 772 "myObject.intProperty == 0"), sTrue); 773 QCOMPARE(m_myObject->intProperty(), 0); 774 QCOMPARE(evalJS("myObject.intProperty = '123';" 775 "myObject.intProperty == 123"), sTrue); 776 QCOMPARE(m_myObject->intProperty(), 123); 777 778 QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao")); 779 QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao")); 780 QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao")); 781 QCOMPARE(evalJS("myObject.stringProperty = 123;" 782 "myObject.stringProperty"), QLatin1String("123")); 783 QCOMPARE(m_myObject->stringProperty(), QLatin1String("123")); 784 QCOMPARE(evalJS("myObject.stringProperty = null"), QString()); 785 QCOMPARE(evalJS("myObject.stringProperty"), QString()); 786 QCOMPARE(m_myObject->stringProperty(), QString()); 787 QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined); 788 QCOMPARE(evalJS("myObject.stringProperty"), QString()); 789 QCOMPARE(m_myObject->stringProperty(), QString()); 790 791 QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);" 792 "myObject.variantProperty").toDouble(), 1234.0); 793 QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0); 794 795 QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);" 796 "myObject.variantProperty"), sTrue); 797 QCOMPARE(m_myObject->variantProperty().toBool(), true); 798 799 QCOMPARE(evalJS("myObject.variantProperty = null;" 800 "myObject.variantProperty.valueOf()"), sUndefined); 801 QCOMPARE(m_myObject->variantProperty(), QVariant()); 802 QCOMPARE(evalJS("myObject.variantProperty = undefined;" 803 "myObject.variantProperty.valueOf()"), sUndefined); 804 QCOMPARE(m_myObject->variantProperty(), QVariant()); 805 806 QCOMPARE(evalJS("myObject.variantProperty = 'foo';" 807 "myObject.variantProperty.valueOf()"), QLatin1String("foo")); 808 QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo"))); 809 QCOMPARE(evalJS("myObject.variantProperty = 42;" 810 "myObject.variantProperty").toDouble(), 42.0); 811 QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0); 812 813 QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];" 814 "myObject.variantListProperty.length == 3"), sTrue); 815 QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue); 816 QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two")); 817 QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue); 818 819 QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];" 820 "myObject.stringListProperty.length == 3"), sTrue); 821 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); 822 QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue); 823 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); 824 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two")); 825 QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString); 826 QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true")); 827 828 // try to delete 829 QCOMPARE(evalJS("delete myObject.intProperty"), sFalse); 830 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); 831 832 QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse); 833 QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0); 834 835 // custom property 836 QCOMPARE(evalJS("myObject.customProperty"), sUndefined); 837 QCOMPARE(evalJS("myObject.customProperty = 123;" 838 "myObject.customProperty == 123"), sTrue); 839 QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty"); 840 QCOMPARE(v.type(), QVariant::Double); 841 QCOMPARE(v.toInt(), 123); 842 843 // non-scriptable property 844 QCOMPARE(m_myObject->hiddenProperty(), 456.0); 845 QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined); 846 QCOMPARE(evalJS("myObject.hiddenProperty = 123;" 847 "myObject.hiddenProperty == 123"), sTrue); 848 QCOMPARE(m_myObject->hiddenProperty(), 456.0); 849 850 // write-only property 851 QCOMPARE(m_myObject->writeOnlyProperty(), 789); 852 QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined); 853 QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;" 854 "typeof myObject.writeOnlyProperty"), sUndefined); 855 QCOMPARE(m_myObject->writeOnlyProperty(), 123); 856 857 // read-only property 858 QCOMPARE(m_myObject->readOnlyProperty(), 987); 859 QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue); 860 QCOMPARE(evalJS("myObject.readOnlyProperty = 654;" 861 "myObject.readOnlyProperty == 987"), sTrue); 862 QCOMPARE(m_myObject->readOnlyProperty(), 987); 863 } 864 865 void tst_QWebFrame::getSetDynamicProperty() 866 { 867 // initially the object does not have the property 868 // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty 869 870 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); 871 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 872 873 // add a dynamic property in C++ 874 QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false); 875 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue); 876 QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue); 877 QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue); 878 879 // property change in script should be reflected in C++ 880 QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';" 881 "myObject.dynamicProperty"), QLatin1String("foo")); 882 QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo")); 883 884 // delete the property (XFAIL - can't delete properties) 885 QEXPECT_FAIL("", "can't delete properties", Continue); 886 QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue); 887 /* 888 QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false); 889 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 890 // QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); 891 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); 892 */ 893 } 894 895 void tst_QWebFrame::getSetChildren() 896 { 897 // initially the object does not have the child 898 // (again, no hasOwnProperty) 899 900 //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); 901 QCOMPARE(evalJS("typeof myObject.child"), sUndefined); 902 903 // add a child 904 MyQObject* child = new MyQObject(m_myObject); 905 child->setObjectName("child"); 906 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue); 907 QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue); 908 909 // add a grandchild 910 MyQObject* grandChild = new MyQObject(child); 911 grandChild->setObjectName("grandChild"); 912 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue); 913 QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue); 914 915 // delete grandchild 916 delete grandChild; 917 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse); 918 QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue); 919 920 // delete child 921 delete child; 922 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); 923 QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue); 924 } 925 926 Q_DECLARE_METATYPE(QVector<int>) 927 Q_DECLARE_METATYPE(QVector<double>) 928 Q_DECLARE_METATYPE(QVector<QString>) 929 930 void tst_QWebFrame::callQtInvokable() 931 { 932 qRegisterMetaType<QObjectList>(); 933 934 m_myObject->resetQtFunctionInvoked(); 935 QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined); 936 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 937 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 938 939 // extra arguments should silently be ignored 940 m_myObject->resetQtFunctionInvoked(); 941 QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined); 942 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 943 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 944 945 m_myObject->resetQtFunctionInvoked(); 946 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined); 947 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 948 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 949 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 950 951 m_myObject->resetQtFunctionInvoked(); 952 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined); 953 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 954 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 955 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 956 957 m_myObject->resetQtFunctionInvoked(); 958 QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined); 959 QCOMPARE(m_myObject->qtFunctionInvoked(), 2); 960 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 961 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123)); 962 963 m_myObject->resetQtFunctionInvoked(); 964 QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined); 965 QCOMPARE(m_myObject->qtFunctionInvoked(), 3); 966 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 967 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); 968 969 m_myObject->resetQtFunctionInvoked(); 970 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined); 971 QCOMPARE(m_myObject->qtFunctionInvoked(), 4); 972 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 973 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); 974 975 m_myObject->resetQtFunctionInvoked(); 976 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined); 977 QCOMPARE(m_myObject->qtFunctionInvoked(), 4); 978 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 979 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5); 980 981 m_myObject->resetQtFunctionInvoked(); 982 QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined); 983 QCOMPARE(m_myObject->qtFunctionInvoked(), 52); 984 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 985 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true); 986 987 m_myObject->resetQtFunctionInvoked(); 988 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined); 989 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 990 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 991 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao")); 992 993 m_myObject->resetQtFunctionInvoked(); 994 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined); 995 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 996 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 997 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); 998 999 m_myObject->resetQtFunctionInvoked(); 1000 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined); 1001 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1002 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1003 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); 1004 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); 1005 1006 m_myObject->resetQtFunctionInvoked(); 1007 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined); 1008 QCOMPARE(m_myObject->qtFunctionInvoked(), 5); 1009 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1010 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); 1011 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); 1012 1013 m_myObject->resetQtFunctionInvoked(); 1014 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined); 1015 QCOMPARE(m_myObject->qtFunctionInvoked(), 6); 1016 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1017 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1018 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); 1019 1020 m_myObject->resetQtFunctionInvoked(); 1021 QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123")); 1022 QCOMPARE(m_myObject->qtFunctionInvoked(), 7); 1023 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1024 1025 m_myObject->resetQtFunctionInvoked(); 1026 QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456")); 1027 QCOMPARE(m_myObject->qtFunctionInvoked(), 39); 1028 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1029 1030 m_myObject->resetQtFunctionInvoked(); 1031 QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao")); 1032 QCOMPARE(m_myObject->qtFunctionInvoked(), 8); 1033 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); 1034 1035 m_myObject->resetQtFunctionInvoked(); 1036 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined); 1037 QCOMPARE(m_myObject->qtFunctionInvoked(), 9); 1038 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1039 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1040 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); 1041 1042 m_myObject->resetQtFunctionInvoked(); 1043 QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined); 1044 QCOMPARE(m_myObject->qtFunctionInvoked(), 44); 1045 m_myObject->resetQtFunctionInvoked(); 1046 { 1047 QString type; 1048 QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type); 1049 QCOMPARE(type, sError); 1050 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n myInvokableWithVoidStarArg(void*)")); 1051 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1052 } 1053 1054 m_myObject->resetQtFunctionInvoked(); 1055 { 1056 QString type; 1057 QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type); 1058 QCOMPARE(type, sError); 1059 QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)")); 1060 } 1061 1062 m_myObject->resetQtFunctionInvoked(); 1063 { 1064 QString type; 1065 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type); 1066 QCOMPARE(type, sUndefined); 1067 QCOMPARE(m_myObject->qtFunctionInvoked(), 47); 1068 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1069 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1070 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello")); 1071 } 1072 1073 m_myObject->resetQtFunctionInvoked(); 1074 { 1075 QString type; 1076 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type); 1077 QCOMPARE(type, sUndefined); 1078 QCOMPARE(m_myObject->qtFunctionInvoked(), 47); 1079 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); 1080 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); 1081 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString()); 1082 } 1083 1084 // calling function that returns (const)ref 1085 m_myObject->resetQtFunctionInvoked(); 1086 { 1087 QString type; 1088 QString ret = evalJS("typeof myObject.myInvokableReturningRef()"); 1089 QCOMPARE(ret, sUndefined); 1090 //QVERIFY(!m_engine->hasUncaughtException()); 1091 QCOMPARE(m_myObject->qtFunctionInvoked(), 48); 1092 } 1093 1094 m_myObject->resetQtFunctionInvoked(); 1095 { 1096 QString type; 1097 QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()"); 1098 QCOMPARE(ret, sUndefined); 1099 //QVERIFY(!m_engine->hasUncaughtException()); 1100 QCOMPARE(m_myObject->qtFunctionInvoked(), 49); 1101 } 1102 1103 m_myObject->resetQtFunctionInvoked(); 1104 { 1105 QString type; 1106 QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type); 1107 QCOMPARE(m_myObject->qtFunctionInvoked(), 13); 1108 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1109 QCOMPARE(type, sObject); 1110 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); 1111 } 1112 1113 m_myObject->resetQtFunctionInvoked(); 1114 { 1115 QString type; 1116 QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type); 1117 QCOMPARE(m_myObject->qtFunctionInvoked(), 14); 1118 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1119 QCOMPARE(type, sArray); 1120 QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList 1121 QVariantList vl = qvariant_cast<QVariantList>(ret); 1122 QCOMPARE(vl.count(), 1); 1123 } 1124 1125 m_myObject->resetQtFunctionInvoked(); 1126 { 1127 QString type; 1128 m_myObject->setVariantProperty(QVariant(123)); 1129 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type); 1130 QCOMPARE(type, sNumber); 1131 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1132 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1133 QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty()); 1134 QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int 1135 QCOMPARE(ret.toInt(),123); 1136 } 1137 1138 m_myObject->resetQtFunctionInvoked(); 1139 { 1140 QString type; 1141 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type); 1142 QCOMPARE(type, sObject); 1143 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1144 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1145 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); 1146 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); 1147 } 1148 1149 m_myObject->resetQtFunctionInvoked(); 1150 { 1151 QString type; 1152 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type); 1153 QCOMPARE(type, sObject); 1154 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1155 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1156 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); 1157 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); 1158 } 1159 1160 /* XFAIL - variant support 1161 m_myObject->resetQtFunctionInvoked(); 1162 { 1163 m_myObject->setVariantProperty(qVariantFromValue(QBrush())); 1164 QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)"); 1165 QVERIFY(ret.isVariant()); 1166 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1167 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1168 QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0)); 1169 QCOMPARE(ret.toVariant(), m_myObject->variantProperty()); 1170 } 1171 */ 1172 1173 m_myObject->resetQtFunctionInvoked(); 1174 { 1175 QString type; 1176 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type); 1177 QCOMPARE(type, sNumber); 1178 QCOMPARE(m_myObject->qtFunctionInvoked(), 15); 1179 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1180 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123)); 1181 QCOMPARE(ret.userType(), int(QMetaType::Double)); 1182 QCOMPARE(ret.toInt(),123); 1183 } 1184 1185 m_myObject->resetQtFunctionInvoked(); 1186 { 1187 QString type; 1188 QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type); 1189 QCOMPARE(m_myObject->qtFunctionInvoked(), 16); 1190 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1191 1192 QVariant v = m_myObject->qtFunctionActuals().at(0); 1193 QCOMPARE(v.userType(), int(QMetaType::QVariantMap)); 1194 1195 QVariantMap vmap = qvariant_cast<QVariantMap>(v); 1196 QCOMPARE(vmap.keys().size(), 2); 1197 QCOMPARE(vmap.keys().at(0), QLatin1String("a")); 1198 QCOMPARE(vmap.value("a"), QVariant(123)); 1199 QCOMPARE(vmap.keys().at(1), QLatin1String("b")); 1200 QCOMPARE(vmap.value("b"), QVariant("ciao")); 1201 1202 QCOMPARE(type, sObject); 1203 1204 QCOMPARE(ret.userType(), int(QMetaType::QVariantMap)); 1205 vmap = qvariant_cast<QVariantMap>(ret); 1206 QCOMPARE(vmap.keys().size(), 2); 1207 QCOMPARE(vmap.keys().at(0), QLatin1String("a")); 1208 QCOMPARE(vmap.value("a"), QVariant(123)); 1209 QCOMPARE(vmap.keys().at(1), QLatin1String("b")); 1210 QCOMPARE(vmap.value("b"), QVariant("ciao")); 1211 } 1212 1213 m_myObject->resetQtFunctionInvoked(); 1214 { 1215 QString type; 1216 QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type); 1217 QCOMPARE(m_myObject->qtFunctionInvoked(), 17); 1218 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1219 QVariant v = m_myObject->qtFunctionActuals().at(0); 1220 QCOMPARE(v.userType(), qMetaTypeId<QList<int> >()); 1221 QList<int> ilst = qvariant_cast<QList<int> >(v); 1222 QCOMPARE(ilst.size(), 2); 1223 QCOMPARE(ilst.at(0), 1); 1224 QCOMPARE(ilst.at(1), 5); 1225 1226 QCOMPARE(type, sArray); 1227 QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist 1228 QVariantList vlst = qvariant_cast<QVariantList>(ret); 1229 QCOMPARE(vlst.size(), 2); 1230 QCOMPARE(vlst.at(0).toInt(), 1); 1231 QCOMPARE(vlst.at(1).toInt(), 5); 1232 } 1233 1234 m_myObject->resetQtFunctionInvoked(); 1235 { 1236 QString type; 1237 QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type); 1238 QCOMPARE(m_myObject->qtFunctionInvoked(), 18); 1239 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1240 QVariant v = m_myObject->qtFunctionActuals().at(0); 1241 QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); 1242 QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject); 1243 1244 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); 1245 QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject); 1246 1247 QCOMPARE(type, sObject); 1248 } 1249 1250 m_myObject->resetQtFunctionInvoked(); 1251 { 1252 // no implicit conversion from integer to QObject* 1253 QString type; 1254 evalJS("myObject.myInvokableWithQObjectStarArg(123)", type); 1255 QCOMPARE(type, sError); 1256 } 1257 1258 /* 1259 m_myObject->resetQtFunctionInvoked(); 1260 { 1261 QString fun = evalJS("myObject.myInvokableWithQBrushArg"); 1262 Q_ASSERT(fun.isFunction()); 1263 QColor color(10, 20, 30, 40); 1264 // QColor should be converted to a QBrush 1265 QVariant ret = fun.call(QString(), QStringList() 1266 << qScriptValueFromValue(m_engine, color)); 1267 QCOMPARE(m_myObject->qtFunctionInvoked(), 19); 1268 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1269 QVariant v = m_myObject->qtFunctionActuals().at(0); 1270 QCOMPARE(v.userType(), int(QMetaType::QBrush)); 1271 QCOMPARE(qvariant_cast<QColor>(v), color); 1272 1273 QCOMPARE(qscriptvalue_cast<QColor>(ret), color); 1274 } 1275 */ 1276 1277 // private slots should not be part of the QObject binding 1278 QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined); 1279 1280 // protected slots should be fine 1281 m_myObject->resetQtFunctionInvoked(); 1282 evalJS("myObject.myProtectedSlot()"); 1283 QCOMPARE(m_myObject->qtFunctionInvoked(), 36); 1284 1285 // call with too few arguments 1286 { 1287 QString type; 1288 QString ret = evalJS("myObject.myInvokableWithIntArg()", type); 1289 QCOMPARE(type, sError); 1290 QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)")); 1291 } 1292 1293 // call function where not all types have been registered 1294 m_myObject->resetQtFunctionInvoked(); 1295 { 1296 QString type; 1297 QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type); 1298 QCOMPARE(type, sError); 1299 QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'")); 1300 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1301 } 1302 1303 // call function with incompatible argument type 1304 m_myObject->resetQtFunctionInvoked(); 1305 { 1306 QString type; 1307 QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type); 1308 QCOMPARE(type, sError); 1309 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)")); 1310 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1311 } 1312 } 1313 1314 void tst_QWebFrame::connectAndDisconnect() 1315 { 1316 // connect(function) 1317 QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction); 1318 QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction); 1319 QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction); 1320 1321 { 1322 QString type; 1323 evalJS("myObject.mySignal.connect(123)", type); 1324 QCOMPARE(type, sError); 1325 } 1326 1327 evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }"); 1328 1329 QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined); 1330 1331 evalJS("gotSignal = false"); 1332 evalJS("myObject.mySignal()"); 1333 QCOMPARE(evalJS("gotSignal"), sTrue); 1334 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1335 QCOMPARE(evalJS("signalSender"),evalJS("myObject")); 1336 QCOMPARE(evalJS("slotThisObject == window"), sTrue); 1337 1338 evalJS("gotSignal = false"); 1339 m_myObject->emitMySignal(); 1340 QCOMPARE(evalJS("gotSignal"), sTrue); 1341 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1342 1343 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined); 1344 1345 evalJS("gotSignal = false"); 1346 m_myObject->emitMySignalWithIntArg(123); 1347 QCOMPARE(evalJS("gotSignal"), sTrue); 1348 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1349 QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue); 1350 1351 QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined); 1352 { 1353 QString type; 1354 evalJS("myObject.mySignal.disconnect(myHandler)", type); 1355 QCOMPARE(type, sError); 1356 } 1357 1358 evalJS("gotSignal = false"); 1359 QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined); 1360 m_myObject->emitMySignal2(true); 1361 QCOMPARE(evalJS("gotSignal"), sTrue); 1362 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1363 QCOMPARE(evalJS("signalArgs[0]"), sTrue); 1364 1365 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined); 1366 1367 QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction); 1368 QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction); 1369 QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction); 1370 1371 QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined); 1372 1373 evalJS("gotSignal = false"); 1374 m_myObject->emitMySignal2(); 1375 QCOMPARE(evalJS("gotSignal"), sTrue); 1376 1377 QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined); 1378 1379 // connect(object, function) 1380 evalJS("otherObject = { name:'foo' }"); 1381 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); 1382 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); 1383 evalJS("gotSignal = false"); 1384 m_myObject->emitMySignal(); 1385 QCOMPARE(evalJS("gotSignal"), sFalse); 1386 1387 { 1388 QString type; 1389 evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type); 1390 QCOMPARE(type, sError); 1391 } 1392 1393 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); 1394 evalJS("gotSignal = false"); 1395 m_myObject->emitMySignal(); 1396 QCOMPARE(evalJS("gotSignal"), sTrue); 1397 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); 1398 QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject")); 1399 QCOMPARE(evalJS("signalSender"),evalJS("myObject")); 1400 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo")); 1401 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); 1402 1403 evalJS("yetAnotherObject = { name:'bar', func : function() { } }"); 1404 QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined); 1405 evalJS("gotSignal = false"); 1406 m_myObject->emitMySignal2(true); 1407 QCOMPARE(evalJS("gotSignal"), sTrue); 1408 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1409 QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue); 1410 QCOMPARE(evalJS("signalSender == myObject"), sTrue); 1411 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar")); 1412 QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined); 1413 1414 QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined); 1415 evalJS("gotSignal = false"); 1416 m_myObject->emitMySignal2(true); 1417 QCOMPARE(evalJS("gotSignal"), sTrue); 1418 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); 1419 QCOMPARE(evalJS("slotThisObject == myObject"), sTrue); 1420 QCOMPARE(evalJS("signalSender == myObject"), sTrue); 1421 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined); 1422 1423 // connect(obj, string) 1424 QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined); 1425 QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined); 1426 QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined); 1427 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined); 1428 1429 // check that emitting signals from script works 1430 1431 // no arguments 1432 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); 1433 m_myObject->resetQtFunctionInvoked(); 1434 QCOMPARE(evalJS("myObject.mySignal()"), sUndefined); 1435 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1436 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined); 1437 1438 // one argument 1439 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined); 1440 m_myObject->resetQtFunctionInvoked(); 1441 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1442 QCOMPARE(m_myObject->qtFunctionInvoked(), 21); 1443 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1444 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1445 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined); 1446 1447 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined); 1448 m_myObject->resetQtFunctionInvoked(); 1449 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1450 QCOMPARE(m_myObject->qtFunctionInvoked(), 22); 1451 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1452 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0); 1453 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined); 1454 1455 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined); 1456 m_myObject->resetQtFunctionInvoked(); 1457 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1458 QCOMPARE(m_myObject->qtFunctionInvoked(), 23); 1459 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1460 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); 1461 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined); 1462 1463 // connecting to overloaded slot 1464 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined); 1465 m_myObject->resetQtFunctionInvoked(); 1466 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); 1467 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload 1468 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1469 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); 1470 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined); 1471 1472 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined); 1473 m_myObject->resetQtFunctionInvoked(); 1474 QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined); 1475 QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload 1476 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1477 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); 1478 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined); 1479 1480 // erroneous input 1481 { 1482 // ### QtScript adds .connect to all functions, WebKit does only to signals/slots 1483 QString type; 1484 QString ret = evalJS("(function() { }).connect()", type); 1485 QCOMPARE(type, sError); 1486 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function.")); 1487 } 1488 { 1489 QString type; 1490 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect()", type); 1491 QCOMPARE(type, sError); 1492 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function.")); 1493 } 1494 1495 { 1496 QString type; 1497 QString ret = evalJS("(function() { }).connect(123)", type); 1498 QCOMPARE(type, sError); 1499 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function.")); 1500 } 1501 { 1502 QString type; 1503 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect(123)", type); 1504 QCOMPARE(type, sError); 1505 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function.")); 1506 } 1507 1508 { 1509 QString type; 1510 QString ret = evalJS("myObject.myInvokable.connect(123)", type); 1511 QCOMPARE(type, sError); 1512 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); 1513 } 1514 { 1515 QString type; 1516 QString ret = evalJS("myObject.myInvokable.connect(function() { })", type); 1517 QCOMPARE(type, sError); 1518 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); 1519 } 1520 1521 { 1522 QString type; 1523 QString ret = evalJS("myObject.mySignal.connect(123)", type); 1524 QCOMPARE(type, sError); 1525 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function")); 1526 } 1527 1528 { 1529 QString type; 1530 QString ret = evalJS("myObject.mySignal.disconnect()", type); 1531 QCOMPARE(type, sError); 1532 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); 1533 } 1534 { 1535 QString type; 1536 QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect; o.disconnect()", type); 1537 QCOMPARE(type, sError); 1538 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); 1539 } 1540 1541 /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots 1542 { 1543 QString type; 1544 QString ret = evalJS("(function() { }).disconnect(123)", type); 1545 QCOMPARE(type, sError); 1546 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal")); 1547 } 1548 */ 1549 1550 { 1551 QString type; 1552 QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type); 1553 QCOMPARE(type, sError); 1554 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1555 } 1556 1557 { 1558 QString type; 1559 QString ret = evalJS("myObject.myInvokable.disconnect(123)", type); 1560 QCOMPARE(type, sError); 1561 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1562 } 1563 { 1564 QString type; 1565 QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type); 1566 QCOMPARE(type, sError); 1567 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); 1568 } 1569 1570 { 1571 QString type; 1572 QString ret = evalJS("myObject.mySignal.disconnect(123)", type); 1573 QCOMPARE(type, sError); 1574 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function")); 1575 } 1576 1577 { 1578 QString type; 1579 QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type); 1580 QCOMPARE(type, sError); 1581 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()")); 1582 } 1583 1584 // when the wrapper dies, the connection stays alive 1585 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); 1586 m_myObject->resetQtFunctionInvoked(); 1587 m_myObject->emitMySignal(); 1588 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1589 evalJS("myObject = null"); 1590 evalJS("gc()"); 1591 m_myObject->resetQtFunctionInvoked(); 1592 m_myObject->emitMySignal(); 1593 QCOMPARE(m_myObject->qtFunctionInvoked(), 20); 1594 } 1595 1596 void tst_QWebFrame::classEnums() 1597 { 1598 // We don't do the meta thing currently, unfortunately!!! 1599 /* 1600 QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue()); 1601 m_engine->globalObject().setProperty("MyQObject", myClass); 1602 1603 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()), 1604 MyQObject::FooPolicy); 1605 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()), 1606 MyQObject::BarPolicy); 1607 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()), 1608 MyQObject::BazPolicy); 1609 1610 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()), 1611 MyQObject::FooStrategy); 1612 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()), 1613 MyQObject::BarStrategy); 1614 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()), 1615 MyQObject::BazStrategy); 1616 1617 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()), 1618 MyQObject::NoAbility); 1619 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()), 1620 MyQObject::FooAbility); 1621 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()), 1622 MyQObject::BarAbility); 1623 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()), 1624 MyQObject::BazAbility); 1625 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()), 1626 MyQObject::AllAbility); 1627 1628 // enums from Qt are inherited through prototype 1629 QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()), 1630 Qt::StrongFocus); 1631 QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()), 1632 Qt::Key_Left); 1633 1634 QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject")); 1635 1636 qRegisterMetaType<MyQObject::Policy>("Policy"); 1637 1638 m_myObject->resetQtFunctionInvoked(); 1639 QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined); 1640 QCOMPARE(m_myObject->qtFunctionInvoked(), 10); 1641 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1642 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); 1643 1644 m_myObject->resetQtFunctionInvoked(); 1645 QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined); 1646 QCOMPARE(m_myObject->qtFunctionInvoked(), 36); 1647 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 1648 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); 1649 1650 m_myObject->resetQtFunctionInvoked(); 1651 { 1652 QVariant ret = evalJS("myObject.myInvokableReturningEnum()"); 1653 QCOMPARE(m_myObject->qtFunctionInvoked(), 37); 1654 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1655 QCOMPARE(ret.isVariant()); 1656 } 1657 m_myObject->resetQtFunctionInvoked(); 1658 { 1659 QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()"); 1660 QCOMPARE(m_myObject->qtFunctionInvoked(), 38); 1661 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); 1662 QCOMPARE(ret.isNumber()); 1663 } 1664 */ 1665 } 1666 1667 void tst_QWebFrame::classConstructor() 1668 { 1669 /* 1670 QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine); 1671 m_engine->globalObject().setProperty("MyQObject", myClass); 1672 1673 QString myObj = evalJS("myObj = MyQObject()"); 1674 QObject* qobj = myObj.toQObject(); 1675 QVERIFY(qobj != 0); 1676 QCOMPARE(qobj->metaObject()->className(), "MyQObject"); 1677 QCOMPARE(qobj->parent(), (QObject*)0); 1678 1679 QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine); 1680 m_engine->globalObject().setProperty("QObject", qobjectClass); 1681 1682 QString otherObj = evalJS("otherObj = QObject(myObj)"); 1683 QObject* qqobj = otherObj.toQObject(); 1684 QVERIFY(qqobj != 0); 1685 QCOMPARE(qqobj->metaObject()->className(), "QObject"); 1686 QCOMPARE(qqobj->parent(), qobj); 1687 1688 delete qobj; 1689 */ 1690 } 1691 1692 void tst_QWebFrame::overrideInvokable() 1693 { 1694 m_myObject->resetQtFunctionInvoked(); 1695 QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined); 1696 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1697 1698 /* XFAIL - can't write to functions with RuntimeObject 1699 m_myObject->resetQtFunctionInvoked(); 1700 evalJS("myObject.myInvokable = function() { window.a = 123; }"); 1701 evalJS("myObject.myInvokable()"); 1702 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1703 QCOMPARE(evalJS("window.a").toDouble(), 123.0); 1704 1705 evalJS("myObject.myInvokable = function() { window.a = 456; }"); 1706 evalJS("myObject.myInvokable()"); 1707 QCOMPARE(m_myObject->qtFunctionInvoked(), -1); 1708 QCOMPARE(evalJS("window.a").toDouble(), 456.0); 1709 */ 1710 1711 evalJS("delete myObject.myInvokable"); 1712 evalJS("myObject.myInvokable()"); 1713 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1714 1715 /* XFAIL - ditto 1716 m_myObject->resetQtFunctionInvoked(); 1717 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); 1718 evalJS("myObject.myInvokable(123)"); 1719 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1720 */ 1721 1722 evalJS("delete myObject.myInvokable"); 1723 m_myObject->resetQtFunctionInvoked(); 1724 // this form (with the '()') is read-only 1725 evalJS("myObject['myInvokable()'] = function() { window.a = 123; }"); 1726 evalJS("myObject.myInvokable()"); 1727 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1728 } 1729 1730 void tst_QWebFrame::transferInvokable() 1731 { 1732 /* XFAIL - can't put to functions with RuntimeObject 1733 m_myObject->resetQtFunctionInvoked(); 1734 evalJS("myObject.foozball = myObject.myInvokable"); 1735 evalJS("myObject.foozball()"); 1736 QCOMPARE(m_myObject->qtFunctionInvoked(), 0); 1737 m_myObject->resetQtFunctionInvoked(); 1738 evalJS("myObject.foozball = myObject.myInvokableWithIntArg"); 1739 evalJS("myObject.foozball(123)"); 1740 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1741 m_myObject->resetQtFunctionInvoked(); 1742 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); 1743 evalJS("myObject.myInvokable(123)"); 1744 QCOMPARE(m_myObject->qtFunctionInvoked(), 1); 1745 1746 MyOtherQObject other; 1747 m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other); 1748 evalJS("myOtherObject.foo = myObject.foozball"); 1749 other.resetQtFunctionInvoked(); 1750 evalJS("myOtherObject.foo(456)"); 1751 QCOMPARE(other.qtFunctionInvoked(), 1); 1752 */ 1753 } 1754 1755 void tst_QWebFrame::findChild() 1756 { 1757 /* 1758 QObject* child = new QObject(m_myObject); 1759 child->setObjectName(QLatin1String("myChildObject")); 1760 1761 { 1762 QString result = evalJS("myObject.findChild('noSuchChild')"); 1763 QCOMPARE(result.isNull()); 1764 } 1765 1766 { 1767 QString result = evalJS("myObject.findChild('myChildObject')"); 1768 QCOMPARE(result.isQObject()); 1769 QCOMPARE(result.toQObject(), child); 1770 } 1771 1772 delete child; 1773 */ 1774 } 1775 1776 void tst_QWebFrame::findChildren() 1777 { 1778 /* 1779 QObject* child = new QObject(m_myObject); 1780 child->setObjectName(QLatin1String("myChildObject")); 1781 1782 { 1783 QString result = evalJS("myObject.findChildren('noSuchChild')"); 1784 QCOMPARE(result.isArray()); 1785 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0); 1786 } 1787 1788 { 1789 QString result = evalJS("myObject.findChildren('myChildObject')"); 1790 QCOMPARE(result.isArray()); 1791 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1792 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); 1793 } 1794 1795 QObject* namelessChild = new QObject(m_myObject); 1796 1797 { 1798 QString result = evalJS("myObject.findChildren('myChildObject')"); 1799 QCOMPARE(result.isArray()); 1800 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1801 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); 1802 } 1803 1804 QObject* anotherChild = new QObject(m_myObject); 1805 anotherChild->setObjectName(QLatin1String("anotherChildObject")); 1806 1807 { 1808 QString result = evalJS("myObject.findChildren('anotherChildObject')"); 1809 QCOMPARE(result.isArray()); 1810 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); 1811 QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild); 1812 } 1813 1814 anotherChild->setObjectName(QLatin1String("myChildObject")); 1815 { 1816 QString result = evalJS("myObject.findChildren('myChildObject')"); 1817 QCOMPARE(result.isArray()); 1818 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0); 1819 QObject* o1 = result.property(QLatin1String("0")).toQObject(); 1820 QObject* o2 = result.property(QLatin1String("1")).toQObject(); 1821 if (o1 != child) { 1822 QCOMPARE(o1, anotherChild); 1823 QCOMPARE(o2, child); 1824 } else { 1825 QCOMPARE(o1, child); 1826 QCOMPARE(o2, anotherChild); 1827 } 1828 } 1829 1830 // find all 1831 { 1832 QString result = evalJS("myObject.findChildren()"); 1833 QVERIFY(result.isArray()); 1834 int count = 3; 1835 QCOMPARE(result.property("length"), QLatin1String(count); 1836 for (int i = 0; i < 3; ++i) { 1837 QObject* o = result.property(i).toQObject(); 1838 if (o == namelessChild || o == child || o == anotherChild) 1839 --count; 1840 } 1841 QVERIFY(count == 0); 1842 } 1843 1844 delete anotherChild; 1845 delete namelessChild; 1846 delete child; 1847 */ 1848 } 1849 1850 void tst_QWebFrame::overloadedSlots() 1851 { 1852 // should pick myOverloadedSlot(double) 1853 m_myObject->resetQtFunctionInvoked(); 1854 evalJS("myObject.myOverloadedSlot(10)"); 1855 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); 1856 1857 // should pick myOverloadedSlot(double) 1858 m_myObject->resetQtFunctionInvoked(); 1859 evalJS("myObject.myOverloadedSlot(10.0)"); 1860 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); 1861 1862 // should pick myOverloadedSlot(QString) 1863 m_myObject->resetQtFunctionInvoked(); 1864 evalJS("myObject.myOverloadedSlot('10')"); 1865 QCOMPARE(m_myObject->qtFunctionInvoked(), 29); 1866 1867 // should pick myOverloadedSlot(bool) 1868 m_myObject->resetQtFunctionInvoked(); 1869 evalJS("myObject.myOverloadedSlot(true)"); 1870 QCOMPARE(m_myObject->qtFunctionInvoked(), 25); 1871 1872 // should pick myOverloadedSlot(QDateTime) 1873 m_myObject->resetQtFunctionInvoked(); 1874 evalJS("myObject.myOverloadedSlot(new Date())"); 1875 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 1876 1877 // should pick myOverloadedSlot(QRegExp) 1878 m_myObject->resetQtFunctionInvoked(); 1879 evalJS("myObject.myOverloadedSlot(new RegExp())"); 1880 QCOMPARE(m_myObject->qtFunctionInvoked(), 34); 1881 1882 // should pick myOverloadedSlot(QVariant) 1883 /* XFAIL 1884 m_myObject->resetQtFunctionInvoked(); 1885 QString f = evalJS("myObject.myOverloadedSlot"); 1886 f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao"))); 1887 QCOMPARE(m_myObject->qtFunctionInvoked(), 35); 1888 */ 1889 // should pick myOverloadedSlot(QObject*) 1890 m_myObject->resetQtFunctionInvoked(); 1891 evalJS("myObject.myOverloadedSlot(myObject)"); 1892 QCOMPARE(m_myObject->qtFunctionInvoked(), 41); 1893 1894 // should pick myOverloadedSlot(QObject*) 1895 m_myObject->resetQtFunctionInvoked(); 1896 evalJS("myObject.myOverloadedSlot(null)"); 1897 QCOMPARE(m_myObject->qtFunctionInvoked(), 41); 1898 1899 // should pick myOverloadedSlot(QStringList) 1900 m_myObject->resetQtFunctionInvoked(); 1901 evalJS("myObject.myOverloadedSlot(['hello'])"); 1902 QCOMPARE(m_myObject->qtFunctionInvoked(), 42); 1903 } 1904 1905 void tst_QWebFrame::enumerate_data() 1906 { 1907 QTest::addColumn<QStringList>("expectedNames"); 1908 1909 QTest::newRow("enumerate all") 1910 << (QStringList() 1911 // meta-object-defined properties: 1912 // inherited 1913 << "objectName" 1914 // non-inherited 1915 << "p1" << "p2" << "p4" << "p6" 1916 // dynamic properties 1917 << "dp1" << "dp2" << "dp3" 1918 // inherited slots 1919 << "destroyed(QObject*)" << "destroyed()" 1920 << "deleteLater()" 1921 // not included because it's private: 1922 // << "_q_reregisterTimers(void*)" 1923 // signals 1924 << "mySignal()" 1925 // slots 1926 << "mySlot()" << "myOtherSlot()"); 1927 } 1928 1929 void tst_QWebFrame::enumerate() 1930 { 1931 QFETCH(QStringList, expectedNames); 1932 1933 MyEnumTestQObject enumQObject; 1934 // give it some dynamic properties 1935 enumQObject.setProperty("dp1", "dp1"); 1936 enumQObject.setProperty("dp2", "dp2"); 1937 enumQObject.setProperty("dp3", "dp3"); 1938 m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject); 1939 1940 // enumerate in script 1941 { 1942 evalJS("var enumeratedProperties = []"); 1943 evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }"); 1944 QStringList result = evalJSV("enumeratedProperties").toStringList(); 1945 QCOMPARE(result.size(), expectedNames.size()); 1946 for (int i = 0; i < expectedNames.size(); ++i) 1947 QCOMPARE(result.at(i), expectedNames.at(i)); 1948 } 1949 } 1950 1951 void tst_QWebFrame::objectDeleted() 1952 { 1953 MyQObject* qobj = new MyQObject(); 1954 m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj); 1955 evalJS("bar.objectName = 'foo';"); 1956 QCOMPARE(qobj->objectName(), QLatin1String("foo")); 1957 evalJS("bar.intProperty = 123;"); 1958 QCOMPARE(qobj->intProperty(), 123); 1959 qobj->resetQtFunctionInvoked(); 1960 evalJS("bar.myInvokable.call(bar);"); 1961 QCOMPARE(qobj->qtFunctionInvoked(), 0); 1962 1963 // do this, to ensure that we cache that it implements call 1964 evalJS("bar()"); 1965 1966 // now delete the object 1967 delete qobj; 1968 1969 QCOMPARE(evalJS("typeof bar"), sObject); 1970 1971 // any attempt to access properties of the object should result in an exception 1972 { 1973 QString type; 1974 QString ret = evalJS("bar.objectName", type); 1975 QCOMPARE(type, sError); 1976 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 1977 } 1978 { 1979 QString type; 1980 QString ret = evalJS("bar.objectName = 'foo'", type); 1981 QCOMPARE(type, sError); 1982 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 1983 } 1984 1985 // myInvokable is stored in member table (since we've accessed it before deletion) 1986 { 1987 QString type; 1988 evalJS("bar.myInvokable", type); 1989 QCOMPARE(type, sFunction); 1990 } 1991 1992 { 1993 QString type; 1994 QString ret = evalJS("bar.myInvokable.call(bar);", type); 1995 ret = evalJS("bar.myInvokable(bar)", type); 1996 QCOMPARE(type, sError); 1997 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); 1998 } 1999 // myInvokableWithIntArg is not stored in member table (since we've not accessed it) 2000 { 2001 QString type; 2002 QString ret = evalJS("bar.myInvokableWithIntArg", type); 2003 QCOMPARE(type, sError); 2004 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); 2005 } 2006 2007 // access from script 2008 evalJS("window.o = bar;"); 2009 { 2010 QString type; 2011 QString ret = evalJS("o.objectName", type); 2012 QCOMPARE(type, sError); 2013 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); 2014 } 2015 { 2016 QString type; 2017 QString ret = evalJS("o.myInvokable()", type); 2018 QCOMPARE(type, sError); 2019 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); 2020 } 2021 { 2022 QString type; 2023 QString ret = evalJS("o.myInvokableWithIntArg(10)", type); 2024 QCOMPARE(type, sError); 2025 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); 2026 } 2027 } 2028 2029 void tst_QWebFrame::typeConversion() 2030 { 2031 m_myObject->resetQtFunctionInvoked(); 2032 2033 QDateTime localdt(QDate(2008,1,18), QTime(12,31,0)); 2034 QDateTime utclocaldt = localdt.toUTC(); 2035 QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC); 2036 2037 // Dates in JS (default to local) 2038 evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))"); 2039 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 2040 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 2041 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt); 2042 2043 m_myObject->resetQtFunctionInvoked(); 2044 evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))"); 2045 QCOMPARE(m_myObject->qtFunctionInvoked(), 32); 2046 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); 2047 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt); 2048 2049 // Pushing QDateTimes into JS 2050 // Local 2051 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}"); 2052 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); 2053 m_myObject->emitMySignalWithDateTimeArg(localdt); 2054 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2055 evalJS("delete window.__date_equals"); 2056 m_myObject->emitMySignalWithDateTimeArg(utclocaldt); 2057 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2058 evalJS("delete window.__date_equals"); 2059 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); 2060 2061 // UTC 2062 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }"); 2063 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); 2064 m_myObject->emitMySignalWithDateTimeArg(utcdt); 2065 QCOMPARE(evalJS("window.__date_equals"), sTrue); 2066 evalJS("delete window.__date_equals"); 2067 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); 2068 2069 // ### RegExps 2070 } 2071 2072 class StringListTestObject : public QObject { 2073 Q_OBJECT 2074 public Q_SLOTS: 2075 QVariant stringList() 2076 { 2077 return QStringList() << "Q" << "t"; 2078 }; 2079 }; 2080 2081 void tst_QWebFrame::arrayObjectEnumerable() 2082 { 2083 QWebPage page; 2084 QWebFrame* frame = page.mainFrame(); 2085 QObject* qobject = new StringListTestObject(); 2086 frame->addToJavaScriptWindowObject("test", qobject, QScriptEngine::ScriptOwnership); 2087 2088 const QString script("var stringArray = test.stringList();" 2089 "var result = '';" 2090 "for (var i in stringArray) {" 2091 " result += stringArray[i];" 2092 "}" 2093 "result;"); 2094 QCOMPARE(frame->evaluateJavaScript(script).toString(), QString::fromLatin1("Qt")); 2095 } 2096 2097 void tst_QWebFrame::symmetricUrl() 2098 { 2099 QVERIFY(m_view->url().isEmpty()); 2100 2101 QCOMPARE(m_view->history()->count(), 0); 2102 2103 QUrl dataUrl("data:text/html,<h1>Test"); 2104 2105 m_view->setUrl(dataUrl); 2106 QCOMPARE(m_view->url(), dataUrl); 2107 QCOMPARE(m_view->history()->count(), 0); 2108 2109 // loading is _not_ immediate, so the text isn't set just yet. 2110 QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty()); 2111 2112 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2113 2114 QCOMPARE(m_view->history()->count(), 1); 2115 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test")); 2116 2117 QUrl dataUrl2("data:text/html,<h1>Test2"); 2118 QUrl dataUrl3("data:text/html,<h1>Test3"); 2119 2120 m_view->setUrl(dataUrl2); 2121 m_view->setUrl(dataUrl3); 2122 2123 QCOMPARE(m_view->url(), dataUrl3); 2124 2125 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2126 2127 QCOMPARE(m_view->history()->count(), 2); 2128 2129 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3")); 2130 } 2131 2132 void tst_QWebFrame::progressSignal() 2133 { 2134 QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); 2135 2136 QUrl dataUrl("data:text/html,<h1>Test"); 2137 m_view->setUrl(dataUrl); 2138 2139 ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); 2140 2141 QVERIFY(progressSpy.size() >= 2); 2142 2143 // WebKit defines initialProgressValue as 10%, not 0% 2144 QCOMPARE(progressSpy.first().first().toInt(), 10); 2145 2146 // But we always end at 100% 2147 QCOMPARE(progressSpy.last().first().toInt(), 100); 2148 } 2149 2150 void tst_QWebFrame::urlChange() 2151 { 2152 QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2153 2154 QUrl dataUrl("data:text/html,<h1>Test"); 2155 m_view->setUrl(dataUrl); 2156 2157 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2158 2159 QCOMPARE(urlSpy.size(), 1); 2160 2161 QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); 2162 m_view->setUrl(dataUrl2); 2163 2164 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); 2165 2166 QCOMPARE(urlSpy.size(), 2); 2167 } 2168 2169 2170 void tst_QWebFrame::domCycles() 2171 { 2172 m_view->setHtml("<html><body>"); 2173 QVariant v = m_page->mainFrame()->evaluateJavaScript("document"); 2174 QVERIFY(v.type() == QVariant::Map); 2175 } 2176 2177 class FakeReply : public QNetworkReply { 2178 Q_OBJECT 2179 2180 public: 2181 FakeReply(const QNetworkRequest& request, QObject* parent = 0) 2182 : QNetworkReply(parent) 2183 { 2184 setOperation(QNetworkAccessManager::GetOperation); 2185 setRequest(request); 2186 if (request.url() == QUrl("qrc:/test1.html")) { 2187 setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); 2188 setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); 2189 } 2190 #ifndef QT_NO_OPENSSL 2191 else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) 2192 setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error 2193 #endif 2194 else if (request.url() == QUrl("http://abcdef.abcdef/")) 2195 setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); 2196 2197 open(QIODevice::ReadOnly); 2198 QTimer::singleShot(0, this, SLOT(timeout())); 2199 } 2200 ~FakeReply() 2201 { 2202 close(); 2203 } 2204 virtual void abort() {} 2205 virtual void close() {} 2206 2207 protected: 2208 qint64 readData(char*, qint64) 2209 { 2210 return 0; 2211 } 2212 2213 private slots: 2214 void timeout() 2215 { 2216 if (request().url() == QUrl("qrc://test1.html")) 2217 emit error(this->error()); 2218 else if (request().url() == QUrl("http://abcdef.abcdef/")) 2219 emit metaDataChanged(); 2220 #ifndef QT_NO_OPENSSL 2221 else if (request().url() == QUrl("qrc:/fake-ssl-error.html")) 2222 return; 2223 #endif 2224 2225 emit readyRead(); 2226 emit finished(); 2227 } 2228 }; 2229 2230 class FakeNetworkManager : public QNetworkAccessManager { 2231 Q_OBJECT 2232 2233 public: 2234 FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } 2235 2236 protected: 2237 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) 2238 { 2239 QString url = request.url().toString(); 2240 if (op == QNetworkAccessManager::GetOperation) { 2241 if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/") 2242 return new FakeReply(request, this); 2243 #ifndef QT_NO_OPENSSL 2244 else if (url == "qrc:/fake-ssl-error.html") { 2245 FakeReply* reply = new FakeReply(request, this); 2246 QList<QSslError> errors; 2247 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); 2248 return reply; 2249 } 2250 #endif 2251 } 2252 2253 return QNetworkAccessManager::createRequest(op, request, outgoingData); 2254 } 2255 }; 2256 2257 void tst_QWebFrame::requestedUrl() 2258 { 2259 QWebPage page; 2260 QWebFrame* frame = page.mainFrame(); 2261 2262 // in few seconds, the image should be completely loaded 2263 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2264 FakeNetworkManager* networkManager = new FakeNetworkManager(&page); 2265 page.setNetworkAccessManager(networkManager); 2266 2267 frame->setUrl(QUrl("qrc:/test1.html")); 2268 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2269 QCOMPARE(spy.count(), 1); 2270 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html")); 2271 QCOMPARE(frame->url(), QUrl("qrc:/test2.html")); 2272 2273 frame->setUrl(QUrl("qrc:/non-existent.html")); 2274 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2275 QCOMPARE(spy.count(), 2); 2276 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html")); 2277 QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html")); 2278 2279 frame->setUrl(QUrl("http://abcdef.abcdef")); 2280 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2281 QCOMPARE(spy.count(), 3); 2282 QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/")); 2283 QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/")); 2284 2285 #ifndef QT_NO_OPENSSL 2286 qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); 2287 qRegisterMetaType<QNetworkReply* >("QNetworkReply*"); 2288 2289 QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); 2290 frame->setUrl(QUrl("qrc:/fake-ssl-error.html")); 2291 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2292 QCOMPARE(spy2.count(), 1); 2293 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html")); 2294 QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html")); 2295 #endif 2296 } 2297 2298 void tst_QWebFrame::javaScriptWindowObjectCleared_data() 2299 { 2300 QTest::addColumn<QString>("html"); 2301 QTest::addColumn<int>("signalCount"); 2302 QTest::newRow("with <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 1; 2303 QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; 2304 } 2305 2306 void tst_QWebFrame::javaScriptWindowObjectCleared() 2307 { 2308 QWebPage page; 2309 QWebFrame* frame = page.mainFrame(); 2310 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 2311 QFETCH(QString, html); 2312 frame->setHtml(html); 2313 2314 QFETCH(int, signalCount); 2315 QCOMPARE(spy.count(), signalCount); 2316 } 2317 2318 void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate() 2319 { 2320 QWebPage page; 2321 QWebFrame* frame = page.mainFrame(); 2322 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); 2323 frame->setHtml("<html></html>"); 2324 QCOMPARE(spy.count(), 0); 2325 frame->evaluateJavaScript("var a = 'a';"); 2326 QCOMPARE(spy.count(), 1); 2327 // no new clear for a new script: 2328 frame->evaluateJavaScript("var a = 1;"); 2329 QCOMPARE(spy.count(), 1); 2330 } 2331 2332 void tst_QWebFrame::setHtml() 2333 { 2334 QString html("<html><head></head><body><p>hello world</p></body></html>"); 2335 m_view->page()->mainFrame()->setHtml(html); 2336 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); 2337 } 2338 2339 void tst_QWebFrame::setHtmlWithResource() 2340 { 2341 QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>"); 2342 2343 QWebPage page; 2344 QWebFrame* frame = page.mainFrame(); 2345 2346 // in few seconds, the image should be completey loaded 2347 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2348 frame->setHtml(html); 2349 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2350 QCOMPARE(spy.count(), 1); 2351 2352 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 2353 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 2354 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 2355 2356 QString html2 = 2357 "<html>" 2358 "<head>" 2359 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" 2360 "</head>" 2361 "<body>" 2362 "<p id='idP'>some text</p>" 2363 "</body>" 2364 "</html>"; 2365 2366 // in few seconds, the CSS should be completey loaded 2367 frame->setHtml(html2); 2368 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2369 QCOMPARE(spy.size(), 2); 2370 2371 QWebElement p = frame->documentElement().findAll("p").at(0); 2372 QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red")); 2373 } 2374 2375 void tst_QWebFrame::setHtmlWithBaseURL() 2376 { 2377 if (!QDir(TESTS_SOURCE_DIR).exists()) 2378 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); 2379 2380 QDir::setCurrent(TESTS_SOURCE_DIR); 2381 2382 QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); 2383 2384 QWebPage page; 2385 QWebFrame* frame = page.mainFrame(); 2386 2387 // in few seconds, the image should be completey loaded 2388 QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); 2389 2390 frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); 2391 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2392 QCOMPARE(spy.count(), 1); 2393 2394 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); 2395 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); 2396 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); 2397 2398 // no history item has to be added. 2399 QCOMPARE(m_view->page()->history()->count(), 0); 2400 } 2401 2402 class TestNetworkManager : public QNetworkAccessManager 2403 { 2404 public: 2405 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} 2406 2407 QList<QUrl> requestedUrls; 2408 2409 protected: 2410 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { 2411 requestedUrls.append(request.url()); 2412 QNetworkRequest redirectedRequest = request; 2413 redirectedRequest.setUrl(QUrl("data:text/html,<p>hello")); 2414 return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData); 2415 } 2416 }; 2417 2418 void tst_QWebFrame::ipv6HostEncoding() 2419 { 2420 TestNetworkManager* networkManager = new TestNetworkManager(m_page); 2421 m_page->setNetworkAccessManager(networkManager); 2422 networkManager->requestedUrls.clear(); 2423 2424 QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html"); 2425 m_view->setHtml("<p>Hi", baseUrl); 2426 m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();" 2427 "r.open('GET', 'http://[::1]/test.xml', false);" 2428 "r.send(null);" 2429 ); 2430 QCOMPARE(networkManager->requestedUrls.count(), 1); 2431 QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); 2432 } 2433 2434 void tst_QWebFrame::metaData() 2435 { 2436 m_view->setHtml("<html>" 2437 " <head>" 2438 " <meta name=\"description\" content=\"Test description\">" 2439 " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" 2440 " </head>" 2441 "</html>"); 2442 2443 QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData(); 2444 2445 QCOMPARE(metaData.count(), 2); 2446 2447 QCOMPARE(metaData.value("description"), QString("Test description")); 2448 QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); 2449 QCOMPARE(metaData.value("nonexistant"), QString()); 2450 2451 m_view->setHtml("<html>" 2452 " <head>" 2453 " <meta name=\"samekey\" content=\"FirstValue\">" 2454 " <meta name=\"samekey\" content=\"SecondValue\">" 2455 " </head>" 2456 "</html>"); 2457 2458 metaData = m_view->page()->mainFrame()->metaData(); 2459 2460 QCOMPARE(metaData.count(), 2); 2461 2462 QStringList values = metaData.values("samekey"); 2463 QCOMPARE(values.count(), 2); 2464 2465 QVERIFY(values.contains("FirstValue")); 2466 QVERIFY(values.contains("SecondValue")); 2467 2468 QCOMPARE(metaData.value("nonexistant"), QString()); 2469 } 2470 2471 void tst_QWebFrame::popupFocus() 2472 { 2473 QWebView view; 2474 view.setHtml("<html>" 2475 " <body>" 2476 " <select name=\"select\">" 2477 " <option>1</option>" 2478 " <option>2</option>" 2479 " </select>" 2480 " <input type=\"text\"> </input>" 2481 " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" 2482 "This test checks whether showing and hiding a popup" 2483 "takes the focus away from the webpage." 2484 " </textarea>" 2485 " </body>" 2486 "</html>"); 2487 view.resize(400, 100); 2488 view.show(); 2489 view.setFocus(); 2490 QTRY_VERIFY(view.hasFocus()); 2491 2492 // open the popup by clicking. check if focus is on the popup 2493 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(25, 25)); 2494 QObject* webpopup = firstChildByClassName(&view, "WebCore::QWebPopup"); 2495 QComboBox* combo = qobject_cast<QComboBox*>(webpopup); 2496 QVERIFY(combo != 0); 2497 QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup 2498 2499 // hide the popup and check if focus is on the page 2500 combo->hidePopup(); 2501 QTRY_VERIFY(view.hasFocus() && !combo->view()->hasFocus()); // Focus should be back on the WebView 2502 2503 // double the flashing time, should at least blink once already 2504 int delay = qApp->cursorFlashTime() * 2; 2505 2506 // focus the lineedit and check if it blinks 2507 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(200, 25)); 2508 m_popupTestView = &view; 2509 view.installEventFilter( this ); 2510 QTest::qWait(delay); 2511 QVERIFY2(m_popupTestPaintCount >= 3, 2512 "The input field should have a blinking caret"); 2513 } 2514 2515 void tst_QWebFrame::hitTestContent() 2516 { 2517 QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\">link text</a></body></html>"); 2518 2519 QWebPage page; 2520 QWebFrame* frame = page.mainFrame(); 2521 frame->setHtml(html); 2522 page.setViewportSize(QSize(200, 0)); //no height so link is not visible 2523 QWebHitTestResult result = frame->hitTestContent(QPoint(10, 100)); 2524 QCOMPARE(result.linkText(), QString("link text")); 2525 QWebElement link = result.linkElement(); 2526 QCOMPARE(link.attribute("target"), QString("_foo")); 2527 } 2528 2529 void tst_QWebFrame::jsByteArray() 2530 { 2531 QByteArray ba("hello world"); 2532 m_myObject->setByteArrayProperty(ba); 2533 2534 // read-only property 2535 QCOMPARE(m_myObject->byteArrayProperty(), ba); 2536 QString type; 2537 QVariant v = evalJSV("myObject.byteArrayProperty"); 2538 QCOMPARE(int(v.type()), int(QVariant::ByteArray)); 2539 2540 QCOMPARE(v.toByteArray(), ba); 2541 } 2542 2543 void tst_QWebFrame::ownership() 2544 { 2545 // test ownership 2546 { 2547 QPointer<QObject> ptr = new QObject(); 2548 QVERIFY(ptr != 0); 2549 { 2550 QWebPage page; 2551 QWebFrame* frame = page.mainFrame(); 2552 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership); 2553 } 2554 QVERIFY(ptr == 0); 2555 } 2556 { 2557 QPointer<QObject> ptr = new QObject(); 2558 QVERIFY(ptr != 0); 2559 QObject* before = ptr; 2560 { 2561 QWebPage page; 2562 QWebFrame* frame = page.mainFrame(); 2563 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership); 2564 } 2565 QVERIFY(ptr == before); 2566 delete ptr; 2567 } 2568 { 2569 QObject* parent = new QObject(); 2570 QObject* child = new QObject(parent); 2571 QWebPage page; 2572 QWebFrame* frame = page.mainFrame(); 2573 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership); 2574 QVariant v = frame->evaluateJavaScript("test"); 2575 QCOMPARE(qvariant_cast<QObject*>(v), child); 2576 delete parent; 2577 v = frame->evaluateJavaScript("test"); 2578 QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0); 2579 } 2580 { 2581 QPointer<QObject> ptr = new QObject(); 2582 QVERIFY(ptr != 0); 2583 { 2584 QWebPage page; 2585 QWebFrame* frame = page.mainFrame(); 2586 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership); 2587 } 2588 // no parent, so it should be like ScriptOwnership 2589 QVERIFY(ptr == 0); 2590 } 2591 { 2592 QObject* parent = new QObject(); 2593 QPointer<QObject> child = new QObject(parent); 2594 QVERIFY(child != 0); 2595 { 2596 QWebPage page; 2597 QWebFrame* frame = page.mainFrame(); 2598 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership); 2599 } 2600 // has parent, so it should be like QtOwnership 2601 QVERIFY(child != 0); 2602 delete parent; 2603 } 2604 } 2605 2606 void tst_QWebFrame::nullValue() 2607 { 2608 QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null"); 2609 QVERIFY(v.isNull()); 2610 } 2611 2612 void tst_QWebFrame::baseUrl_data() 2613 { 2614 QTest::addColumn<QString>("html"); 2615 QTest::addColumn<QUrl>("loadUrl"); 2616 QTest::addColumn<QUrl>("url"); 2617 QTest::addColumn<QUrl>("baseUrl"); 2618 2619 QTest::newRow("null") << QString() << QUrl() 2620 << QUrl("about:blank") << QUrl("about:blank"); 2621 2622 QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") 2623 << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); 2624 2625 QString html = "<html>" 2626 "<head>" 2627 "<base href=\"http://foobaz.bar/\" />" 2628 "</head>" 2629 "</html>"; 2630 QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") 2631 << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); 2632 } 2633 2634 void tst_QWebFrame::baseUrl() 2635 { 2636 QFETCH(QString, html); 2637 QFETCH(QUrl, loadUrl); 2638 QFETCH(QUrl, url); 2639 QFETCH(QUrl, baseUrl); 2640 2641 m_page->mainFrame()->setHtml(html, loadUrl); 2642 QCOMPARE(m_page->mainFrame()->url(), url); 2643 QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl); 2644 } 2645 2646 void tst_QWebFrame::hasSetFocus() 2647 { 2648 QString html("<html><body><p>top</p>" \ 2649 "<iframe width='80%' height='30%'/>" \ 2650 "</body></html>"); 2651 2652 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); 2653 m_page->mainFrame()->setHtml(html); 2654 2655 waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200); 2656 QCOMPARE(loadSpy.size(), 1); 2657 2658 QList<QWebFrame*> children = m_page->mainFrame()->childFrames(); 2659 QWebFrame* frame = children.at(0); 2660 QString innerHtml("<html><body><p>another iframe</p>" \ 2661 "<iframe width='80%' height='30%'/>" \ 2662 "</body></html>"); 2663 frame->setHtml(innerHtml); 2664 2665 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); 2666 QCOMPARE(loadSpy.size(), 2); 2667 2668 m_page->mainFrame()->setFocus(); 2669 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 2670 2671 for (int i = 0; i < children.size(); ++i) { 2672 children.at(i)->setFocus(); 2673 QTRY_VERIFY(children.at(i)->hasFocus()); 2674 QVERIFY(!m_page->mainFrame()->hasFocus()); 2675 } 2676 2677 m_page->mainFrame()->setFocus(); 2678 QTRY_VERIFY(m_page->mainFrame()->hasFocus()); 2679 } 2680 2681 void tst_QWebFrame::render() 2682 { 2683 QString html("<html>" \ 2684 "<head><style>" \ 2685 "body, iframe { margin: 0px; border: none; }" \ 2686 "</style></head>" \ 2687 "<body><iframe width='100px' height='100px'/></body>" \ 2688 "</html>"); 2689 2690 QWebPage page; 2691 page.mainFrame()->setHtml(html); 2692 2693 QList<QWebFrame*> frames = page.mainFrame()->childFrames(); 2694 QWebFrame *frame = frames.at(0); 2695 QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>"); 2696 frame->setHtml(innerHtml); 2697 2698 QPicture picture; 2699 2700 QSize size = page.mainFrame()->contentsSize(); 2701 page.setViewportSize(size); 2702 2703 // render contents layer only (the iframe is smaller than the image, so it will have scrollbars) 2704 QPainter painter1(&picture); 2705 frame->render(&painter1, QWebFrame::ContentsLayer); 2706 painter1.end(); 2707 2708 QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width()); 2709 QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height()); 2710 2711 // render everything, should be the size of the iframe 2712 QPainter painter2(&picture); 2713 frame->render(&painter2, QWebFrame::AllLayers); 2714 painter2.end(); 2715 2716 QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px 2717 QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px 2718 } 2719 2720 void tst_QWebFrame::scrollPosition() 2721 { 2722 // enlarged image in a small viewport, to provoke the scrollbars to appear 2723 QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); 2724 2725 QWebPage page; 2726 page.setViewportSize(QSize(200, 200)); 2727 2728 QWebFrame* frame = page.mainFrame(); 2729 frame->setHtml(html); 2730 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 2731 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 2732 2733 // try to set the scroll offset programmatically 2734 frame->setScrollPosition(QPoint(23, 29)); 2735 QCOMPARE(frame->scrollPosition().x(), 23); 2736 QCOMPARE(frame->scrollPosition().y(), 29); 2737 2738 int x = frame->evaluateJavaScript("window.scrollX").toInt(); 2739 int y = frame->evaluateJavaScript("window.scrollY").toInt(); 2740 QCOMPARE(x, 23); 2741 QCOMPARE(y, 29); 2742 } 2743 2744 void tst_QWebFrame::evaluateWillCauseRepaint() 2745 { 2746 QWebView view; 2747 QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" 2748 "junk</div>bottom</body></html>"); 2749 view.setHtml(html); 2750 view.show(); 2751 2752 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) 2753 QTest::qWaitForWindowShown(&view); 2754 #else 2755 QTest::qWait(2000); 2756 #endif 2757 2758 view.page()->mainFrame()->evaluateJavaScript( 2759 "document.getElementById('junk').style.display = 'none';"); 2760 2761 ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); 2762 } 2763 2764 class TestFactory : public QObject 2765 { 2766 Q_OBJECT 2767 public: 2768 TestFactory() 2769 : obj(0), counter(0) 2770 {} 2771 2772 Q_INVOKABLE QObject* getNewObject() 2773 { 2774 delete obj; 2775 obj = new QObject(this); 2776 obj->setObjectName(QLatin1String("test") + QString::number(++counter)); 2777 return obj; 2778 2779 } 2780 2781 QObject* obj; 2782 int counter; 2783 }; 2784 2785 void tst_QWebFrame::qObjectWrapperWithSameIdentity() 2786 { 2787 m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>" 2788 "<body><span id='span1'>test</span></body>"); 2789 2790 QWebFrame* mainFrame = m_view->page()->mainFrame(); 2791 QCOMPARE(mainFrame->toPlainText(), QString("test")); 2792 2793 mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership); 2794 2795 mainFrame->evaluateJavaScript("triggerBug();"); 2796 QCOMPARE(mainFrame->toPlainText(), QString("test1")); 2797 2798 mainFrame->evaluateJavaScript("triggerBug();"); 2799 QCOMPARE(mainFrame->toPlainText(), QString("test2")); 2800 } 2801 2802 void tst_QWebFrame::scrollRecursively() 2803 { 2804 // The test content is 2805 // a nested frame set 2806 // The main frame scrolls 2807 // and has two children 2808 // an iframe and a div overflow 2809 // both scroll 2810 QWebView webView; 2811 QWebPage* webPage = webView.page(); 2812 QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); 2813 QUrl url = QUrl("qrc:///testiframe.html"); 2814 webPage->mainFrame()->load(url); 2815 QTRY_COMPARE(loadSpy.count(), 1); 2816 2817 QList<QWebFrame*> children = webPage->mainFrame()->childFrames(); 2818 QVERIFY(children.count() == 1); 2819 2820 // 1st test 2821 // call scrollRecursively over mainframe 2822 // verify scrolled 2823 // verify scroll postion changed 2824 QPoint scrollPosition(webPage->mainFrame()->scrollPosition()); 2825 QVERIFY(webPage->mainFrame()->scrollRecursively(10, 10)); 2826 QVERIFY(scrollPosition != webPage->mainFrame()->scrollPosition()); 2827 2828 // 2nd test 2829 // call scrollRecursively over child iframe 2830 // verify scrolled 2831 // verify child scroll position changed 2832 // verify parent's scroll position did not change 2833 scrollPosition = webPage->mainFrame()->scrollPosition(); 2834 QPoint childScrollPosition = children.at(0)->scrollPosition(); 2835 QVERIFY(children.at(0)->scrollRecursively(10, 10)); 2836 QVERIFY(scrollPosition == webPage->mainFrame()->scrollPosition()); 2837 QVERIFY(childScrollPosition != children.at(0)->scrollPosition()); 2838 2839 // 3rd test 2840 // call scrollRecursively over div overflow 2841 // verify scrolled == true 2842 // verify parent and child frame's scroll postion did not change 2843 QWebElement div = webPage->mainFrame()->documentElement().findFirst("#content1"); 2844 QMouseEvent evpres(QEvent::MouseMove, div.geometry().center(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); 2845 webPage->event(&evpres); 2846 scrollPosition = webPage->mainFrame()->scrollPosition(); 2847 childScrollPosition = children.at(0)->scrollPosition(); 2848 QVERIFY(webPage->mainFrame()->scrollRecursively(5, 5)); 2849 QVERIFY(childScrollPosition == children.at(0)->scrollPosition()); 2850 QVERIFY(scrollPosition == webPage->mainFrame()->scrollPosition()); 2851 2852 // 4th test 2853 // call scrollRecursively twice over childs iframe 2854 // verify scrolled == true first time 2855 // verify parent's scroll == true second time 2856 // verify parent and childs scroll position changed 2857 childScrollPosition = children.at(0)->scrollPosition(); 2858 QVERIFY(children.at(0)->scrollRecursively(-10, -10)); 2859 QVERIFY(childScrollPosition != children.at(0)->scrollPosition()); 2860 scrollPosition = webPage->mainFrame()->scrollPosition(); 2861 QVERIFY(children.at(0)->scrollRecursively(-10, -10)); 2862 QVERIFY(scrollPosition != webPage->mainFrame()->scrollPosition()); 2863 2864 } 2865 2866 void tst_QWebFrame::introspectQtMethods_data() 2867 { 2868 QTest::addColumn<QString>("objectExpression"); 2869 QTest::addColumn<QString>("methodName"); 2870 QTest::addColumn<QStringList>("expectedPropertyNames"); 2871 2872 QTest::newRow("myObject.mySignal") 2873 << "myObject" << "mySignal" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 2874 QTest::newRow("myObject.mySlot") 2875 << "myObject" << "mySlot" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 2876 QTest::newRow("myObject.myInvokable") 2877 << "myObject" << "myInvokable" << (QStringList() << "connect" << "disconnect" << "length" << "name"); 2878 QTest::newRow("myObject.mySignal.connect") 2879 << "myObject.mySignal" << "connect" << (QStringList() << "length" << "name"); 2880 QTest::newRow("myObject.mySignal.disconnect") 2881 << "myObject.mySignal" << "disconnect" << (QStringList() << "length" << "name"); 2882 } 2883 2884 void tst_QWebFrame::introspectQtMethods() 2885 { 2886 QFETCH(QString, objectExpression); 2887 QFETCH(QString, methodName); 2888 QFETCH(QStringList, expectedPropertyNames); 2889 2890 QString methodLookup = QString::fromLatin1("%0['%1']").arg(objectExpression).arg(methodName); 2891 QCOMPARE(evalJSV(QString::fromLatin1("Object.getOwnPropertyNames(%0).sort()").arg(methodLookup)).toStringList(), expectedPropertyNames); 2892 2893 for (int i = 0; i < expectedPropertyNames.size(); ++i) { 2894 QString name = expectedPropertyNames.at(i); 2895 QCOMPARE(evalJS(QString::fromLatin1("%0.hasOwnProperty('%1')").arg(methodLookup).arg(name)), sTrue); 2896 evalJS(QString::fromLatin1("var descriptor = Object.getOwnPropertyDescriptor(%0, '%1')").arg(methodLookup).arg(name)); 2897 QCOMPARE(evalJS("typeof descriptor"), QString::fromLatin1("object")); 2898 QCOMPARE(evalJS("descriptor.get"), sUndefined); 2899 QCOMPARE(evalJS("descriptor.set"), sUndefined); 2900 QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue); 2901 QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse); 2902 QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse); 2903 } 2904 2905 QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty()); 2906 } 2907 2908 QTEST_MAIN(tst_QWebFrame) 2909 #include "tst_qwebframe.moc" 2910