2 Copyright (C) 2008,2009 Nokia Corporation and/or its subsidiary(-ies)
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.
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.
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.
21 #include <QtTest/QtTest>
24 #include <qwebelement.h>
27 #include <qwebframe.h>
28 #include <qwebhistory.h>
29 #include <QAbstractItemView>
30 #include <QApplication>
34 #include <QNetworkRequest>
35 #include <QNetworkReply>
37 #include <qsslerror.h>
44 Q_DECLARE_METATYPE(CustomType)
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)
53 class MyQObject : public QObject
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(QVariantMap variantMapProperty READ variantMapProperty WRITE setVariantMapProperty)
61 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
62 Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty)
63 Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty)
64 Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty)
65 Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false)
66 Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty)
67 Q_PROPERTY(int readOnlyProperty READ readOnlyProperty)
68 Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
69 Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType)
70 Q_PROPERTY(QWebElement webElementProperty READ webElementProperty WRITE setWebElementProperty)
71 Q_PROPERTY(QObject* objectStarProperty READ objectStarProperty WRITE setObjectStarProperty)
72 Q_ENUMS(Policy Strategy)
93 AllAbility = FooAbility | BarAbility | BazAbility
96 Q_DECLARE_FLAGS(Ability, AbilityFlag)
98 MyQObject(QObject* parent = 0)
101 m_variantValue(QLatin1String("foo")),
102 m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))),
103 m_stringValue(QLatin1String("bar")),
104 m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")),
105 m_brushValue(QColor(10, 20, 30, 40)),
106 m_hiddenValue(456.0),
107 m_writeOnlyValue(789),
108 m_readOnlyValue(987),
110 m_qtFunctionInvoked(-1)
112 m_variantMapValue.insert("a", QVariant(123));
113 m_variantMapValue.insert("b", QVariant(QLatin1String("foo")));
114 m_variantMapValue.insert("c", QVariant::fromValue<QObject*>(this));
119 int intProperty() const {
122 void setIntProperty(int value) {
126 QVariant variantProperty() const {
127 return m_variantValue;
129 void setVariantProperty(const QVariant &value) {
130 m_variantValue = value;
133 QVariantList variantListProperty() const {
134 return m_variantListValue;
136 void setVariantListProperty(const QVariantList &value) {
137 m_variantListValue = value;
140 QVariantMap variantMapProperty() const {
141 return m_variantMapValue;
143 void setVariantMapProperty(const QVariantMap &value) {
144 m_variantMapValue = value;
147 QString stringProperty() const {
148 return m_stringValue;
150 void setStringProperty(const QString &value) {
151 m_stringValue = value;
154 QStringList stringListProperty() const {
155 return m_stringListValue;
157 void setStringListProperty(const QStringList &value) {
158 m_stringListValue = value;
161 QByteArray byteArrayProperty() const {
162 return m_byteArrayValue;
164 void setByteArrayProperty(const QByteArray &value) {
165 m_byteArrayValue = value;
168 QBrush brushProperty() const {
171 Q_INVOKABLE void setBrushProperty(const QBrush &value) {
172 m_brushValue = value;
175 double hiddenProperty() const {
176 return m_hiddenValue;
178 void setHiddenProperty(double value) {
179 m_hiddenValue = value;
182 int writeOnlyProperty() const {
183 return m_writeOnlyValue;
185 void setWriteOnlyProperty(int value) {
186 m_writeOnlyValue = value;
189 int readOnlyProperty() const {
190 return m_readOnlyValue;
193 QKeySequence shortcut() const {
196 void setShortcut(const QKeySequence &seq) {
200 QWebElement webElementProperty() const {
204 void setWebElementProperty(const QWebElement& element) {
205 m_webElement = element;
208 CustomType propWithCustomType() const {
211 void setPropWithCustomType(const CustomType &c) {
215 QObject* objectStarProperty() const {
219 void setObjectStarProperty(QObject* object) {
220 m_objectStar = object;
224 int qtFunctionInvoked() const {
225 return m_qtFunctionInvoked;
228 QVariantList qtFunctionActuals() const {
232 void resetQtFunctionInvoked() {
233 m_qtFunctionInvoked = -1;
237 Q_INVOKABLE void myInvokable() {
238 m_qtFunctionInvoked = 0;
240 Q_INVOKABLE void myInvokableWithIntArg(int arg) {
241 m_qtFunctionInvoked = 1;
244 Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) {
245 m_qtFunctionInvoked = 2;
248 Q_INVOKABLE void myInvokableWithFloatArg(float arg) {
249 m_qtFunctionInvoked = 3;
252 Q_INVOKABLE void myInvokableWithDoubleArg(double arg) {
253 m_qtFunctionInvoked = 4;
256 Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) {
257 m_qtFunctionInvoked = 5;
260 Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) {
261 m_qtFunctionInvoked = 6;
262 m_actuals << arg1 << arg2;
264 Q_INVOKABLE int myInvokableReturningInt() {
265 m_qtFunctionInvoked = 7;
268 Q_INVOKABLE qlonglong myInvokableReturningLongLong() {
269 m_qtFunctionInvoked = 39;
272 Q_INVOKABLE QString myInvokableReturningString() {
273 m_qtFunctionInvoked = 8;
274 return QLatin1String("ciao");
276 Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload
277 m_qtFunctionInvoked = 9;
278 m_actuals << arg1 << arg2;
280 Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) {
281 m_qtFunctionInvoked = 10;
284 Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) {
285 m_qtFunctionInvoked = 36;
288 Q_INVOKABLE Policy myInvokableReturningEnum() {
289 m_qtFunctionInvoked = 37;
292 Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() {
293 m_qtFunctionInvoked = 38;
296 Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() {
297 m_qtFunctionInvoked = 11;
298 return QVector<int>();
300 Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) {
301 m_qtFunctionInvoked = 12;
303 Q_INVOKABLE QObject* myInvokableReturningQObjectStar() {
304 m_qtFunctionInvoked = 13;
307 Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) {
308 m_qtFunctionInvoked = 14;
309 m_actuals << qVariantFromValue(lst);
312 Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) {
313 m_qtFunctionInvoked = 15;
317 Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) {
318 m_qtFunctionInvoked = 16;
322 Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) {
323 m_qtFunctionInvoked = 17;
324 m_actuals << qVariantFromValue(lst);
327 Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) {
328 m_qtFunctionInvoked = 18;
329 m_actuals << qVariantFromValue(obj);
332 Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) {
333 m_qtFunctionInvoked = 19;
334 m_actuals << qVariantFromValue(brush);
337 Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) {
338 m_qtFunctionInvoked = 43;
339 m_actuals << qVariantFromValue(style);
341 Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) {
342 m_qtFunctionInvoked = 44;
343 m_actuals << qVariantFromValue(arg);
345 Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) {
346 m_qtFunctionInvoked = 45;
347 m_actuals << qVariantFromValue(arg);
349 Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) {
350 m_qtFunctionInvoked = 46;
351 m_actuals << qVariantFromValue(arg);
353 Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") {
354 m_qtFunctionInvoked = 47;
355 m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2);
357 Q_INVOKABLE QObject& myInvokableReturningRef() {
358 m_qtFunctionInvoked = 48;
361 Q_INVOKABLE const QObject& myInvokableReturningConstRef() const {
362 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49;
365 Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) {
366 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50;
367 m_actuals << qVariantFromValue(arg);
369 Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) {
370 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51;
371 m_actuals << qVariantFromValue(arg);
373 Q_INVOKABLE void myInvokableWithBoolArg(bool arg) {
374 m_qtFunctionInvoked = 52;
378 void emitMySignal() {
381 void emitMySignalWithIntArg(int arg) {
382 emit mySignalWithIntArg(arg);
384 void emitMySignal2(bool arg) {
387 void emitMySignal2() {
390 void emitMySignalWithDateTimeArg(QDateTime dt) {
391 emit mySignalWithDateTimeArg(dt);
393 void emitMySignalWithRegexArg(QRegExp r) {
394 emit mySignalWithRegexArg(r);
399 m_qtFunctionInvoked = 20;
401 void mySlotWithIntArg(int arg) {
402 m_qtFunctionInvoked = 21;
405 void mySlotWithDoubleArg(double arg) {
406 m_qtFunctionInvoked = 22;
409 void mySlotWithStringArg(const QString &arg) {
410 m_qtFunctionInvoked = 23;
414 void myOverloadedSlot() {
415 m_qtFunctionInvoked = 24;
417 void myOverloadedSlot(QObject* arg) {
418 m_qtFunctionInvoked = 41;
419 m_actuals << qVariantFromValue(arg);
421 void myOverloadedSlot(bool arg) {
422 m_qtFunctionInvoked = 25;
425 void myOverloadedSlot(const QStringList &arg) {
426 m_qtFunctionInvoked = 42;
429 void myOverloadedSlot(double arg) {
430 m_qtFunctionInvoked = 26;
433 void myOverloadedSlot(float arg) {
434 m_qtFunctionInvoked = 27;
437 void myOverloadedSlot(int arg) {
438 m_qtFunctionInvoked = 28;
441 void myOverloadedSlot(const QString &arg) {
442 m_qtFunctionInvoked = 29;
445 void myOverloadedSlot(const QColor &arg) {
446 m_qtFunctionInvoked = 30;
449 void myOverloadedSlot(const QBrush &arg) {
450 m_qtFunctionInvoked = 31;
453 void myOverloadedSlot(const QDateTime &arg) {
454 m_qtFunctionInvoked = 32;
457 void myOverloadedSlot(const QDate &arg) {
458 m_qtFunctionInvoked = 33;
461 void myOverloadedSlot(const QRegExp &arg) {
462 m_qtFunctionInvoked = 34;
465 void myOverloadedSlot(const QVariant &arg) {
466 m_qtFunctionInvoked = 35;
469 void myOverloadedSlot(const QWebElement &arg) {
470 m_qtFunctionInvoked = 36;
471 m_actuals << QVariant::fromValue<QWebElement>(arg);
474 void qscript_call(int arg) {
475 m_qtFunctionInvoked = 40;
480 void myProtectedSlot() {
481 m_qtFunctionInvoked = 36;
485 void myPrivateSlot() { }
489 void mySignalWithIntArg(int arg);
490 void mySignalWithDoubleArg(double arg);
491 void mySignal2(bool arg = false);
492 void mySignalWithDateTimeArg(QDateTime dt);
493 void mySignalWithRegexArg(QRegExp r);
497 QVariant m_variantValue;
498 QVariantList m_variantListValue;
499 QVariantMap m_variantMapValue;
500 QString m_stringValue;
501 QStringList m_stringListValue;
502 QByteArray m_byteArrayValue;
504 double m_hiddenValue;
505 int m_writeOnlyValue;
507 QKeySequence m_shortcut;
508 QWebElement m_webElement;
509 CustomType m_customType;
510 QObject* m_objectStar;
511 int m_qtFunctionInvoked;
512 QVariantList m_actuals;
515 class MyOtherQObject : public MyQObject
518 MyOtherQObject(QObject* parent = 0)
519 : MyQObject(parent) { }
522 class MyEnumTestQObject : public QObject
525 Q_PROPERTY(QString p1 READ p1)
526 Q_PROPERTY(QString p2 READ p2)
527 Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false)
528 Q_PROPERTY(QString p4 READ p4)
529 Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false)
530 Q_PROPERTY(QString p6 READ p6)
532 MyEnumTestQObject(QObject* parent = 0)
533 : QObject(parent) { }
535 return QLatin1String("p1");
538 return QLatin1String("p2");
541 return QLatin1String("p3");
544 return QLatin1String("p4");
547 return QLatin1String("p5");
550 return QLatin1String("p5");
554 void myOtherSlot() { }
559 class tst_QWebFrame : public QObject
565 virtual ~tst_QWebFrame();
566 bool eventFilter(QObject* watched, QEvent* event);
573 void getSetStaticProperty();
574 void getSetDynamicProperty();
575 void getSetChildren();
576 void callQtInvokable();
577 void connectAndDisconnect();
579 void classConstructor();
580 void overrideInvokable();
581 void transferInvokable();
584 void overloadedSlots();
585 void enumerate_data();
587 void objectDeleted();
588 void typeConversion();
589 void arrayObjectEnumerable();
591 void progressSignal();
595 void javaScriptWindowObjectCleared_data();
596 void javaScriptWindowObjectCleared();
597 void javaScriptWindowObjectClearedOnEvaluate();
599 void setHtmlWithResource();
600 void setHtmlWithBaseURL();
601 void ipv6HostEncoding();
603 #if !defined(Q_WS_MAEMO_5)
604 // as maemo 5 does not use QComboBoxes to implement the popups
605 // this test does not make sense for it.
608 void inputFieldFocus();
609 void hitTestContent();
617 void scrollPosition();
618 void scrollToAnchor();
619 void scrollbarsOff();
620 void evaluateWillCauseRepaint();
621 void qObjectWrapperWithSameIdentity();
622 void introspectQtMethods_data();
623 void introspectQtMethods();
626 QString evalJS(const QString&s) {
627 // Convert an undefined return variant to the string "undefined"
628 QVariant ret = evalJSV(s);
629 if (ret.userType() == QMetaType::Void)
632 return ret.toString();
634 QVariant evalJSV(const QString &s) {
635 return m_page->mainFrame()->evaluateJavaScript(s);
638 QString evalJS(const QString&s, QString& type) {
639 return evalJSV(s, type).toString();
641 QVariant evalJSV(const QString &s, QString& type) {
642 // As a special measure, if we get an exception we set the type to 'error'
643 // (in ecma, an Error object has typeof object, but qtscript has a convenience function)
644 // Similarly, an array is an object, but we'd prefer to have a type of 'array'
645 // Also, consider a QMetaType::Void QVariant to be undefined
647 escaped.replace('\'', "\\'"); // Don't preescape your single quotes!
648 evalJS("var retvalue;\
651 retvalue = eval('" + escaped + "'); \
652 typevalue = typeof retvalue; \
653 if (retvalue instanceof Array) \
654 typevalue = 'array'; \
657 retvalue = e.name + ': ' + e.message;\
658 typevalue = 'error';\
660 QVariant ret = evalJSV("retvalue");
661 if (ret.userType() != QMetaType::Void)
662 type = evalJS("typevalue");
664 ret = QString("undefined");
667 evalJS("delete retvalue; delete typevalue");
670 QObject* firstChildByClassName(QObject* parent, const char* className) {
671 const QObjectList & children = parent->children();
672 foreach (QObject* child, children) {
673 if (!strcmp(child->metaObject()->className(), className)) {
681 const QString sFalse;
682 const QString sUndefined;
683 const QString sArray;
684 const QString sFunction;
685 const QString sError;
686 const QString sString;
687 const QString sObject;
688 const QString sNumber;
693 MyQObject* m_myObject;
694 QWebView* m_inputFieldsTestView;
695 int m_inputFieldTestPaintCount;
698 tst_QWebFrame::tst_QWebFrame()
699 : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"),
700 sString("string"), sObject("object"), sNumber("number"), m_inputFieldsTestView(0), m_inputFieldTestPaintCount(0)
704 tst_QWebFrame::~tst_QWebFrame()
708 bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event)
710 // used on the inputFieldFocus test
711 if (watched == m_inputFieldsTestView) {
712 if (event->type() == QEvent::Paint)
713 m_inputFieldTestPaintCount++;
715 return QObject::eventFilter(watched, event);
718 void tst_QWebFrame::init()
720 m_view = new QWebView();
721 m_page = m_view->page();
722 m_myObject = new MyQObject();
723 m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject);
726 void tst_QWebFrame::cleanup()
732 void tst_QWebFrame::getSetStaticProperty()
734 m_page->mainFrame()->setHtml("<html><head><body></body></html>");
735 QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined);
737 // initial value (set in MyQObject constructor)
740 QVariant ret = evalJSV("myObject.intProperty", type);
741 QCOMPARE(type, sNumber);
742 QCOMPARE(ret.type(), QVariant::Double);
743 QCOMPARE(ret.toInt(), 123);
745 QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue);
749 QVariant ret = evalJSV("myObject.variantProperty", type);
750 QCOMPARE(type, sString);
751 QCOMPARE(ret.type(), QVariant::String);
752 QCOMPARE(ret.toString(), QLatin1String("foo"));
754 QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue);
758 QVariant ret = evalJSV("myObject.stringProperty", type);
759 QCOMPARE(type, sString);
760 QCOMPARE(ret.type(), QVariant::String);
761 QCOMPARE(ret.toString(), QLatin1String("bar"));
763 QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue);
767 QVariant ret = evalJSV("myObject.variantListProperty", type);
768 QCOMPARE(type, sArray);
769 QCOMPARE(ret.type(), QVariant::List);
770 QVariantList vl = ret.value<QVariantList>();
771 QCOMPARE(vl.size(), 2);
772 QCOMPARE(vl.at(0).toInt(), 123);
773 QCOMPARE(vl.at(1).toString(), QLatin1String("foo"));
775 QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue);
776 QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue);
777 QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue);
781 QVariant ret = evalJSV("myObject.variantMapProperty", type);
782 QCOMPARE(type, sObject);
783 QCOMPARE(ret.type(), QVariant::Map);
784 QVariantMap vm = ret.value<QVariantMap>();
785 QCOMPARE(vm.size(), 3);
786 QCOMPARE(vm.value("a").toInt(), 123);
787 QCOMPARE(vm.value("b").toString(), QLatin1String("foo"));
788 QCOMPARE(vm.value("c").value<QObject*>(), static_cast<QObject*>(m_myObject));
790 QCOMPARE(evalJS("myObject.variantMapProperty.a === 123"), sTrue);
791 QCOMPARE(evalJS("myObject.variantMapProperty.b === 'foo'"), sTrue);
792 QCOMPARE(evalJS("myObject.variantMapProperty.c.variantMapProperty.b === 'foo'"), sTrue);
796 QVariant ret = evalJSV("myObject.stringListProperty", type);
797 QCOMPARE(type, sArray);
798 QCOMPARE(ret.type(), QVariant::List);
799 QVariantList vl = ret.value<QVariantList>();
800 QCOMPARE(vl.size(), 2);
801 QCOMPARE(vl.at(0).toString(), QLatin1String("zig"));
802 QCOMPARE(vl.at(1).toString(), QLatin1String("zag"));
804 QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue);
805 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
806 QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig"));
807 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
808 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag"));
810 // property change in C++ should be reflected in script
811 m_myObject->setIntProperty(456);
812 QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue);
813 m_myObject->setIntProperty(789);
814 QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue);
816 m_myObject->setVariantProperty(QLatin1String("bar"));
817 QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue);
818 m_myObject->setVariantProperty(42);
819 QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue);
820 m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
822 // QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant);
824 m_myObject->setStringProperty(QLatin1String("baz"));
825 QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue);
826 m_myObject->setStringProperty(QLatin1String("zab"));
827 QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue);
829 // property change in script should be reflected in C++
830 QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123"));
831 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
832 QCOMPARE(m_myObject->intProperty(), 123);
833 QCOMPARE(evalJS("myObject.intProperty = 'ciao!';"
834 "myObject.intProperty == 0"), sTrue);
835 QCOMPARE(m_myObject->intProperty(), 0);
836 QCOMPARE(evalJS("myObject.intProperty = '123';"
837 "myObject.intProperty == 123"), sTrue);
838 QCOMPARE(m_myObject->intProperty(), 123);
840 QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao"));
841 QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao"));
842 QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao"));
843 QCOMPARE(evalJS("myObject.stringProperty = 123;"
844 "myObject.stringProperty"), QLatin1String("123"));
845 QCOMPARE(m_myObject->stringProperty(), QLatin1String("123"));
846 QCOMPARE(evalJS("myObject.stringProperty = null"), QString());
847 QCOMPARE(evalJS("myObject.stringProperty"), QString());
848 QCOMPARE(m_myObject->stringProperty(), QString());
849 QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined);
850 QCOMPARE(evalJS("myObject.stringProperty"), QString());
851 QCOMPARE(m_myObject->stringProperty(), QString());
853 QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);"
854 "myObject.variantProperty").toDouble(), 1234.0);
855 QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0);
857 QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);"
858 "myObject.variantProperty"), sTrue);
859 QCOMPARE(m_myObject->variantProperty().toBool(), true);
861 QCOMPARE(evalJS("myObject.variantProperty = null;"
862 "myObject.variantProperty.valueOf()"), sUndefined);
863 QCOMPARE(m_myObject->variantProperty(), QVariant());
864 QCOMPARE(evalJS("myObject.variantProperty = undefined;"
865 "myObject.variantProperty.valueOf()"), sUndefined);
866 QCOMPARE(m_myObject->variantProperty(), QVariant());
868 QCOMPARE(evalJS("myObject.variantProperty = 'foo';"
869 "myObject.variantProperty.valueOf()"), QLatin1String("foo"));
870 QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo")));
871 QCOMPARE(evalJS("myObject.variantProperty = 42;"
872 "myObject.variantProperty").toDouble(), 42.0);
873 QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0);
875 QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];"
876 "myObject.variantListProperty.length == 3"), sTrue);
877 QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue);
878 QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two"));
879 QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue);
881 QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];"
882 "myObject.stringListProperty.length == 3"), sTrue);
883 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
884 QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue);
885 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
886 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two"));
887 QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString);
888 QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true"));
889 evalJS("myObject.webElementProperty=document.body;");
890 QCOMPARE(evalJS("myObject.webElementProperty.tagName"), QLatin1String("BODY"));
893 QCOMPARE(evalJS("delete myObject.intProperty"), sFalse);
894 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
896 QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse);
897 QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0);
900 QCOMPARE(evalJS("myObject.customProperty"), sUndefined);
901 QCOMPARE(evalJS("myObject.customProperty = 123;"
902 "myObject.customProperty == 123"), sTrue);
903 QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty");
904 QCOMPARE(v.type(), QVariant::Double);
905 QCOMPARE(v.toInt(), 123);
907 // non-scriptable property
908 QCOMPARE(m_myObject->hiddenProperty(), 456.0);
909 QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined);
910 QCOMPARE(evalJS("myObject.hiddenProperty = 123;"
911 "myObject.hiddenProperty == 123"), sTrue);
912 QCOMPARE(m_myObject->hiddenProperty(), 456.0);
914 // write-only property
915 QCOMPARE(m_myObject->writeOnlyProperty(), 789);
916 QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined);
917 QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;"
918 "typeof myObject.writeOnlyProperty"), sUndefined);
919 QCOMPARE(m_myObject->writeOnlyProperty(), 123);
921 // read-only property
922 QCOMPARE(m_myObject->readOnlyProperty(), 987);
923 QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue);
924 QCOMPARE(evalJS("myObject.readOnlyProperty = 654;"
925 "myObject.readOnlyProperty == 987"), sTrue);
926 QCOMPARE(m_myObject->readOnlyProperty(), 987);
929 m_myObject->setObjectStarProperty(0);
930 QCOMPARE(m_myObject->objectStarProperty(), (QObject*)0);
931 QCOMPARE(evalJS("myObject.objectStarProperty == null"), sTrue);
932 QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject);
933 QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sFalse);
934 QCOMPARE(evalJS("String(myObject.objectStarProperty) == 'null'"), sTrue);
935 QCOMPARE(evalJS("myObject.objectStarProperty.objectStarProperty"),
937 m_myObject->setObjectStarProperty(this);
938 QCOMPARE(evalJS("myObject.objectStarProperty != null"), sTrue);
939 QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject);
940 QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sTrue);
941 QCOMPARE(evalJS("String(myObject.objectStarProperty) != 'null'"), sTrue);
944 void tst_QWebFrame::getSetDynamicProperty()
946 // initially the object does not have the property
947 // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty
949 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
950 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
952 // add a dynamic property in C++
953 QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
954 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue);
955 QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue);
956 QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue);
958 // property change in script should be reflected in C++
959 QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';"
960 "myObject.dynamicProperty"), QLatin1String("foo"));
961 QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo"));
963 // delete the property (XFAIL - can't delete properties)
964 QEXPECT_FAIL("", "can't delete properties", Continue);
965 QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue);
967 QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false);
968 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
969 // QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
970 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
974 void tst_QWebFrame::getSetChildren()
976 // initially the object does not have the child
977 // (again, no hasOwnProperty)
979 //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
980 QCOMPARE(evalJS("typeof myObject.child"), sUndefined);
983 MyQObject* child = new MyQObject(m_myObject);
984 child->setObjectName("child");
985 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue);
986 QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue);
989 MyQObject* grandChild = new MyQObject(child);
990 grandChild->setObjectName("grandChild");
991 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue);
992 QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue);
996 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse);
997 QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue);
1001 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
1002 QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue);
1005 Q_DECLARE_METATYPE(QVector<int>)
1006 Q_DECLARE_METATYPE(QVector<double>)
1007 Q_DECLARE_METATYPE(QVector<QString>)
1009 void tst_QWebFrame::callQtInvokable()
1011 qRegisterMetaType<QObjectList>();
1013 m_myObject->resetQtFunctionInvoked();
1014 QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined);
1015 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1016 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1018 // extra arguments should silently be ignored
1019 m_myObject->resetQtFunctionInvoked();
1020 QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined);
1021 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1022 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1024 m_myObject->resetQtFunctionInvoked();
1025 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined);
1026 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1027 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1028 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1030 m_myObject->resetQtFunctionInvoked();
1031 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined);
1032 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1033 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1034 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1036 m_myObject->resetQtFunctionInvoked();
1037 QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined);
1038 QCOMPARE(m_myObject->qtFunctionInvoked(), 2);
1039 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1040 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123));
1042 m_myObject->resetQtFunctionInvoked();
1043 QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined);
1044 QCOMPARE(m_myObject->qtFunctionInvoked(), 3);
1045 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1046 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
1048 m_myObject->resetQtFunctionInvoked();
1049 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined);
1050 QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
1051 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1052 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
1054 m_myObject->resetQtFunctionInvoked();
1055 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined);
1056 QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
1057 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1058 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5);
1060 m_myObject->resetQtFunctionInvoked();
1061 QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined);
1062 QCOMPARE(m_myObject->qtFunctionInvoked(), 52);
1063 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1064 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true);
1066 m_myObject->resetQtFunctionInvoked();
1067 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined);
1068 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1069 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1070 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao"));
1072 m_myObject->resetQtFunctionInvoked();
1073 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined);
1074 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1075 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1076 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1078 m_myObject->resetQtFunctionInvoked();
1079 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined);
1080 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1081 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1082 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1083 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1085 m_myObject->resetQtFunctionInvoked();
1086 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined);
1087 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1088 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1089 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1090 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1092 m_myObject->resetQtFunctionInvoked();
1093 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined);
1094 QCOMPARE(m_myObject->qtFunctionInvoked(), 6);
1095 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1096 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1097 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1099 m_myObject->resetQtFunctionInvoked();
1100 QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123"));
1101 QCOMPARE(m_myObject->qtFunctionInvoked(), 7);
1102 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1104 m_myObject->resetQtFunctionInvoked();
1105 QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456"));
1106 QCOMPARE(m_myObject->qtFunctionInvoked(), 39);
1107 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1109 m_myObject->resetQtFunctionInvoked();
1110 QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao"));
1111 QCOMPARE(m_myObject->qtFunctionInvoked(), 8);
1112 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1114 m_myObject->resetQtFunctionInvoked();
1115 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined);
1116 QCOMPARE(m_myObject->qtFunctionInvoked(), 9);
1117 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1118 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1119 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1121 m_myObject->resetQtFunctionInvoked();
1122 QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined);
1123 QCOMPARE(m_myObject->qtFunctionInvoked(), 44);
1124 m_myObject->resetQtFunctionInvoked();
1127 QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type);
1128 QCOMPARE(type, sError);
1129 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n myInvokableWithVoidStarArg(void*)"));
1130 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1133 m_myObject->resetQtFunctionInvoked();
1136 QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type);
1137 QCOMPARE(type, sError);
1138 QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)"));
1141 m_myObject->resetQtFunctionInvoked();
1144 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type);
1145 QCOMPARE(type, sUndefined);
1146 QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1147 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1148 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1149 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello"));
1152 m_myObject->resetQtFunctionInvoked();
1155 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type);
1156 QCOMPARE(type, sUndefined);
1157 QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1158 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1159 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1160 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString());
1163 // calling function that returns (const)ref
1164 m_myObject->resetQtFunctionInvoked();
1167 QString ret = evalJS("typeof myObject.myInvokableReturningRef()");
1168 QCOMPARE(ret, sUndefined);
1169 //QVERIFY(!m_engine->hasUncaughtException());
1170 QCOMPARE(m_myObject->qtFunctionInvoked(), 48);
1173 m_myObject->resetQtFunctionInvoked();
1176 QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()");
1177 QCOMPARE(ret, sUndefined);
1178 //QVERIFY(!m_engine->hasUncaughtException());
1179 QCOMPARE(m_myObject->qtFunctionInvoked(), 49);
1182 m_myObject->resetQtFunctionInvoked();
1185 QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type);
1186 QCOMPARE(m_myObject->qtFunctionInvoked(), 13);
1187 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1188 QCOMPARE(type, sObject);
1189 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1192 m_myObject->resetQtFunctionInvoked();
1195 QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type);
1196 QCOMPARE(m_myObject->qtFunctionInvoked(), 14);
1197 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1198 QCOMPARE(type, sArray);
1199 QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList
1200 QVariantList vl = qvariant_cast<QVariantList>(ret);
1201 QCOMPARE(vl.count(), 1);
1204 m_myObject->resetQtFunctionInvoked();
1207 m_myObject->setVariantProperty(QVariant(123));
1208 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type);
1209 QCOMPARE(type, sNumber);
1210 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1211 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1212 QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty());
1213 QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int
1214 QCOMPARE(ret.toInt(),123);
1217 m_myObject->resetQtFunctionInvoked();
1220 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type);
1221 QCOMPARE(type, sObject);
1222 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1223 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1224 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1225 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1228 m_myObject->resetQtFunctionInvoked();
1231 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type);
1232 QCOMPARE(type, sObject);
1233 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1234 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1235 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1236 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1239 /* XFAIL - variant support
1240 m_myObject->resetQtFunctionInvoked();
1242 m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
1243 QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
1244 QVERIFY(ret.isVariant());
1245 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1246 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1247 QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0));
1248 QCOMPARE(ret.toVariant(), m_myObject->variantProperty());
1252 m_myObject->resetQtFunctionInvoked();
1255 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type);
1256 QCOMPARE(type, sNumber);
1257 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1258 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1259 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123));
1260 QCOMPARE(ret.userType(), int(QMetaType::Double));
1261 QCOMPARE(ret.toInt(),123);
1264 m_myObject->resetQtFunctionInvoked();
1267 QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type);
1268 QCOMPARE(m_myObject->qtFunctionInvoked(), 16);
1269 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1271 QVariant v = m_myObject->qtFunctionActuals().at(0);
1272 QCOMPARE(v.userType(), int(QMetaType::QVariantMap));
1274 QVariantMap vmap = qvariant_cast<QVariantMap>(v);
1275 QCOMPARE(vmap.keys().size(), 2);
1276 QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1277 QCOMPARE(vmap.value("a"), QVariant(123));
1278 QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1279 QCOMPARE(vmap.value("b"), QVariant("ciao"));
1281 QCOMPARE(type, sObject);
1283 QCOMPARE(ret.userType(), int(QMetaType::QVariantMap));
1284 vmap = qvariant_cast<QVariantMap>(ret);
1285 QCOMPARE(vmap.keys().size(), 2);
1286 QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1287 QCOMPARE(vmap.value("a"), QVariant(123));
1288 QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1289 QCOMPARE(vmap.value("b"), QVariant("ciao"));
1292 m_myObject->resetQtFunctionInvoked();
1295 QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type);
1296 QCOMPARE(m_myObject->qtFunctionInvoked(), 17);
1297 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1298 QVariant v = m_myObject->qtFunctionActuals().at(0);
1299 QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
1300 QList<int> ilst = qvariant_cast<QList<int> >(v);
1301 QCOMPARE(ilst.size(), 2);
1302 QCOMPARE(ilst.at(0), 1);
1303 QCOMPARE(ilst.at(1), 5);
1305 QCOMPARE(type, sArray);
1306 QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist
1307 QVariantList vlst = qvariant_cast<QVariantList>(ret);
1308 QCOMPARE(vlst.size(), 2);
1309 QCOMPARE(vlst.at(0).toInt(), 1);
1310 QCOMPARE(vlst.at(1).toInt(), 5);
1313 m_myObject->resetQtFunctionInvoked();
1316 QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type);
1317 QCOMPARE(m_myObject->qtFunctionInvoked(), 18);
1318 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1319 QVariant v = m_myObject->qtFunctionActuals().at(0);
1320 QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1321 QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject);
1323 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1324 QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject);
1326 QCOMPARE(type, sObject);
1329 m_myObject->resetQtFunctionInvoked();
1331 // no implicit conversion from integer to QObject*
1333 evalJS("myObject.myInvokableWithQObjectStarArg(123)", type);
1334 QCOMPARE(type, sError);
1338 m_myObject->resetQtFunctionInvoked();
1340 QString fun = evalJS("myObject.myInvokableWithQBrushArg");
1341 Q_ASSERT(fun.isFunction());
1342 QColor color(10, 20, 30, 40);
1343 // QColor should be converted to a QBrush
1344 QVariant ret = fun.call(QString(), QStringList()
1345 << qScriptValueFromValue(m_engine, color));
1346 QCOMPARE(m_myObject->qtFunctionInvoked(), 19);
1347 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1348 QVariant v = m_myObject->qtFunctionActuals().at(0);
1349 QCOMPARE(v.userType(), int(QMetaType::QBrush));
1350 QCOMPARE(qvariant_cast<QColor>(v), color);
1352 QCOMPARE(qscriptvalue_cast<QColor>(ret), color);
1356 // private slots should not be part of the QObject binding
1357 QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined);
1359 // protected slots should be fine
1360 m_myObject->resetQtFunctionInvoked();
1361 evalJS("myObject.myProtectedSlot()");
1362 QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1364 // call with too few arguments
1367 QString ret = evalJS("myObject.myInvokableWithIntArg()", type);
1368 QCOMPARE(type, sError);
1369 QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)"));
1372 // call function where not all types have been registered
1373 m_myObject->resetQtFunctionInvoked();
1376 QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type);
1377 QCOMPARE(type, sError);
1378 QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'"));
1379 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1382 // call function with incompatible argument type
1383 m_myObject->resetQtFunctionInvoked();
1386 QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type);
1387 QCOMPARE(type, sError);
1388 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)"));
1389 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1393 void tst_QWebFrame::connectAndDisconnect()
1395 // connect(function)
1396 QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction);
1397 QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction);
1398 QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction);
1402 evalJS("myObject.mySignal.connect(123)", type);
1403 QCOMPARE(type, sError);
1406 evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }");
1408 QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined);
1410 evalJS("gotSignal = false");
1411 evalJS("myObject.mySignal()");
1412 QCOMPARE(evalJS("gotSignal"), sTrue);
1413 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1414 QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1415 QCOMPARE(evalJS("slotThisObject == window"), sTrue);
1417 evalJS("gotSignal = false");
1418 m_myObject->emitMySignal();
1419 QCOMPARE(evalJS("gotSignal"), sTrue);
1420 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1422 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined);
1424 evalJS("gotSignal = false");
1425 m_myObject->emitMySignalWithIntArg(123);
1426 QCOMPARE(evalJS("gotSignal"), sTrue);
1427 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1428 QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue);
1430 QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined);
1433 evalJS("myObject.mySignal.disconnect(myHandler)", type);
1434 QCOMPARE(type, sError);
1437 evalJS("gotSignal = false");
1438 QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined);
1439 m_myObject->emitMySignal2(true);
1440 QCOMPARE(evalJS("gotSignal"), sTrue);
1441 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1442 QCOMPARE(evalJS("signalArgs[0]"), sTrue);
1444 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined);
1446 QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction);
1447 QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction);
1448 QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction);
1450 QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined);
1452 evalJS("gotSignal = false");
1453 m_myObject->emitMySignal2();
1454 QCOMPARE(evalJS("gotSignal"), sTrue);
1456 QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined);
1458 // connect(object, function)
1459 evalJS("otherObject = { name:'foo' }");
1460 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1461 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1462 evalJS("gotSignal = false");
1463 m_myObject->emitMySignal();
1464 QCOMPARE(evalJS("gotSignal"), sFalse);
1468 evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type);
1469 QCOMPARE(type, sError);
1472 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1473 evalJS("gotSignal = false");
1474 m_myObject->emitMySignal();
1475 QCOMPARE(evalJS("gotSignal"), sTrue);
1476 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1477 QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject"));
1478 QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1479 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo"));
1480 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1482 evalJS("yetAnotherObject = { name:'bar', func : function() { } }");
1483 QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined);
1484 evalJS("gotSignal = false");
1485 m_myObject->emitMySignal2(true);
1486 QCOMPARE(evalJS("gotSignal"), sTrue);
1487 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1488 QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue);
1489 QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1490 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar"));
1491 QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined);
1493 QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined);
1494 evalJS("gotSignal = false");
1495 m_myObject->emitMySignal2(true);
1496 QCOMPARE(evalJS("gotSignal"), sTrue);
1497 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1498 QCOMPARE(evalJS("slotThisObject == myObject"), sTrue);
1499 QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1500 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined);
1502 // connect(obj, string)
1503 QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined);
1504 QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined);
1505 QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined);
1506 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined);
1508 // check that emitting signals from script works
1511 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1512 m_myObject->resetQtFunctionInvoked();
1513 QCOMPARE(evalJS("myObject.mySignal()"), sUndefined);
1514 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1515 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined);
1518 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined);
1519 m_myObject->resetQtFunctionInvoked();
1520 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1521 QCOMPARE(m_myObject->qtFunctionInvoked(), 21);
1522 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1523 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1524 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined);
1526 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined);
1527 m_myObject->resetQtFunctionInvoked();
1528 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1529 QCOMPARE(m_myObject->qtFunctionInvoked(), 22);
1530 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1531 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0);
1532 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined);
1534 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined);
1535 m_myObject->resetQtFunctionInvoked();
1536 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1537 QCOMPARE(m_myObject->qtFunctionInvoked(), 23);
1538 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1539 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1540 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined);
1542 // connecting to overloaded slot
1543 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined);
1544 m_myObject->resetQtFunctionInvoked();
1545 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1546 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload
1547 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1548 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1549 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined);
1551 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1552 m_myObject->resetQtFunctionInvoked();
1553 QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined);
1554 QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
1555 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1556 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1557 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1561 // ### QtScript adds .connect to all functions, WebKit does only to signals/slots
1563 QString ret = evalJS("(function() { }).connect()", type);
1564 QCOMPARE(type, sError);
1565 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1569 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect()", type);
1570 QCOMPARE(type, sError);
1571 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1576 QString ret = evalJS("(function() { }).connect(123)", type);
1577 QCOMPARE(type, sError);
1578 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1582 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect(123)", type);
1583 QCOMPARE(type, sError);
1584 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1589 QString ret = evalJS("myObject.myInvokable.connect(123)", type);
1590 QCOMPARE(type, sError);
1591 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1595 QString ret = evalJS("myObject.myInvokable.connect(function() { })", type);
1596 QCOMPARE(type, sError);
1597 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1602 QString ret = evalJS("myObject.mySignal.connect(123)", type);
1603 QCOMPARE(type, sError);
1604 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function"));
1609 QString ret = evalJS("myObject.mySignal.disconnect()", type);
1610 QCOMPARE(type, sError);
1611 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1615 QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect; o.disconnect()", type);
1616 QCOMPARE(type, sError);
1617 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1620 /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots
1623 QString ret = evalJS("(function() { }).disconnect(123)", type);
1624 QCOMPARE(type, sError);
1625 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal"));
1631 QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type);
1632 QCOMPARE(type, sError);
1633 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1638 QString ret = evalJS("myObject.myInvokable.disconnect(123)", type);
1639 QCOMPARE(type, sError);
1640 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1644 QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type);
1645 QCOMPARE(type, sError);
1646 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1651 QString ret = evalJS("myObject.mySignal.disconnect(123)", type);
1652 QCOMPARE(type, sError);
1653 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function"));
1658 QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type);
1659 QCOMPARE(type, sError);
1660 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()"));
1663 // when the wrapper dies, the connection stays alive
1664 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1665 m_myObject->resetQtFunctionInvoked();
1666 m_myObject->emitMySignal();
1667 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1668 evalJS("myObject = null");
1670 m_myObject->resetQtFunctionInvoked();
1671 m_myObject->emitMySignal();
1672 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1675 void tst_QWebFrame::classEnums()
1677 // We don't do the meta thing currently, unfortunately!!!
1679 QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue());
1680 m_engine->globalObject().setProperty("MyQObject", myClass);
1682 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()),
1683 MyQObject::FooPolicy);
1684 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()),
1685 MyQObject::BarPolicy);
1686 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()),
1687 MyQObject::BazPolicy);
1689 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()),
1690 MyQObject::FooStrategy);
1691 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()),
1692 MyQObject::BarStrategy);
1693 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()),
1694 MyQObject::BazStrategy);
1696 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()),
1697 MyQObject::NoAbility);
1698 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()),
1699 MyQObject::FooAbility);
1700 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()),
1701 MyQObject::BarAbility);
1702 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()),
1703 MyQObject::BazAbility);
1704 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()),
1705 MyQObject::AllAbility);
1707 // enums from Qt are inherited through prototype
1708 QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()),
1710 QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()),
1713 QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject"));
1715 qRegisterMetaType<MyQObject::Policy>("Policy");
1717 m_myObject->resetQtFunctionInvoked();
1718 QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined);
1719 QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
1720 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1721 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1723 m_myObject->resetQtFunctionInvoked();
1724 QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined);
1725 QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1726 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1727 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1729 m_myObject->resetQtFunctionInvoked();
1731 QVariant ret = evalJS("myObject.myInvokableReturningEnum()");
1732 QCOMPARE(m_myObject->qtFunctionInvoked(), 37);
1733 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1734 QCOMPARE(ret.isVariant());
1736 m_myObject->resetQtFunctionInvoked();
1738 QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()");
1739 QCOMPARE(m_myObject->qtFunctionInvoked(), 38);
1740 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1741 QCOMPARE(ret.isNumber());
1746 void tst_QWebFrame::classConstructor()
1749 QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine);
1750 m_engine->globalObject().setProperty("MyQObject", myClass);
1752 QString myObj = evalJS("myObj = MyQObject()");
1753 QObject* qobj = myObj.toQObject();
1755 QCOMPARE(qobj->metaObject()->className(), "MyQObject");
1756 QCOMPARE(qobj->parent(), (QObject*)0);
1758 QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine);
1759 m_engine->globalObject().setProperty("QObject", qobjectClass);
1761 QString otherObj = evalJS("otherObj = QObject(myObj)");
1762 QObject* qqobj = otherObj.toQObject();
1763 QVERIFY(qqobj != 0);
1764 QCOMPARE(qqobj->metaObject()->className(), "QObject");
1765 QCOMPARE(qqobj->parent(), qobj);
1771 void tst_QWebFrame::overrideInvokable()
1773 m_myObject->resetQtFunctionInvoked();
1774 QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined);
1775 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1777 /* XFAIL - can't write to functions with RuntimeObject
1778 m_myObject->resetQtFunctionInvoked();
1779 evalJS("myObject.myInvokable = function() { window.a = 123; }");
1780 evalJS("myObject.myInvokable()");
1781 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1782 QCOMPARE(evalJS("window.a").toDouble(), 123.0);
1784 evalJS("myObject.myInvokable = function() { window.a = 456; }");
1785 evalJS("myObject.myInvokable()");
1786 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1787 QCOMPARE(evalJS("window.a").toDouble(), 456.0);
1790 evalJS("delete myObject.myInvokable");
1791 evalJS("myObject.myInvokable()");
1792 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1795 m_myObject->resetQtFunctionInvoked();
1796 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1797 evalJS("myObject.myInvokable(123)");
1798 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1801 evalJS("delete myObject.myInvokable");
1802 m_myObject->resetQtFunctionInvoked();
1803 // this form (with the '()') is read-only
1804 evalJS("myObject['myInvokable()'] = function() { window.a = 123; }");
1805 evalJS("myObject.myInvokable()");
1806 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1809 void tst_QWebFrame::transferInvokable()
1811 /* XFAIL - can't put to functions with RuntimeObject
1812 m_myObject->resetQtFunctionInvoked();
1813 evalJS("myObject.foozball = myObject.myInvokable");
1814 evalJS("myObject.foozball()");
1815 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1816 m_myObject->resetQtFunctionInvoked();
1817 evalJS("myObject.foozball = myObject.myInvokableWithIntArg");
1818 evalJS("myObject.foozball(123)");
1819 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1820 m_myObject->resetQtFunctionInvoked();
1821 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1822 evalJS("myObject.myInvokable(123)");
1823 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1825 MyOtherQObject other;
1826 m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other);
1827 evalJS("myOtherObject.foo = myObject.foozball");
1828 other.resetQtFunctionInvoked();
1829 evalJS("myOtherObject.foo(456)");
1830 QCOMPARE(other.qtFunctionInvoked(), 1);
1834 void tst_QWebFrame::findChild()
1837 QObject* child = new QObject(m_myObject);
1838 child->setObjectName(QLatin1String("myChildObject"));
1841 QString result = evalJS("myObject.findChild('noSuchChild')");
1842 QCOMPARE(result.isNull());
1846 QString result = evalJS("myObject.findChild('myChildObject')");
1847 QCOMPARE(result.isQObject());
1848 QCOMPARE(result.toQObject(), child);
1855 void tst_QWebFrame::findChildren()
1858 QObject* child = new QObject(m_myObject);
1859 child->setObjectName(QLatin1String("myChildObject"));
1862 QString result = evalJS("myObject.findChildren('noSuchChild')");
1863 QCOMPARE(result.isArray());
1864 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0);
1868 QString result = evalJS("myObject.findChildren('myChildObject')");
1869 QCOMPARE(result.isArray());
1870 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1871 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1874 QObject* namelessChild = new QObject(m_myObject);
1877 QString result = evalJS("myObject.findChildren('myChildObject')");
1878 QCOMPARE(result.isArray());
1879 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1880 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1883 QObject* anotherChild = new QObject(m_myObject);
1884 anotherChild->setObjectName(QLatin1String("anotherChildObject"));
1887 QString result = evalJS("myObject.findChildren('anotherChildObject')");
1888 QCOMPARE(result.isArray());
1889 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1890 QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild);
1893 anotherChild->setObjectName(QLatin1String("myChildObject"));
1895 QString result = evalJS("myObject.findChildren('myChildObject')");
1896 QCOMPARE(result.isArray());
1897 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0);
1898 QObject* o1 = result.property(QLatin1String("0")).toQObject();
1899 QObject* o2 = result.property(QLatin1String("1")).toQObject();
1901 QCOMPARE(o1, anotherChild);
1902 QCOMPARE(o2, child);
1904 QCOMPARE(o1, child);
1905 QCOMPARE(o2, anotherChild);
1911 QString result = evalJS("myObject.findChildren()");
1912 QVERIFY(result.isArray());
1914 QCOMPARE(result.property("length"), QLatin1String(count);
1915 for (int i = 0; i < 3; ++i) {
1916 QObject* o = result.property(i).toQObject();
1917 if (o == namelessChild || o == child || o == anotherChild)
1920 QVERIFY(count == 0);
1923 delete anotherChild;
1924 delete namelessChild;
1929 void tst_QWebFrame::overloadedSlots()
1931 // should pick myOverloadedSlot(double)
1932 m_myObject->resetQtFunctionInvoked();
1933 evalJS("myObject.myOverloadedSlot(10)");
1934 QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1936 // should pick myOverloadedSlot(double)
1937 m_myObject->resetQtFunctionInvoked();
1938 evalJS("myObject.myOverloadedSlot(10.0)");
1939 QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1941 // should pick myOverloadedSlot(QString)
1942 m_myObject->resetQtFunctionInvoked();
1943 evalJS("myObject.myOverloadedSlot('10')");
1944 QCOMPARE(m_myObject->qtFunctionInvoked(), 29);
1946 // should pick myOverloadedSlot(bool)
1947 m_myObject->resetQtFunctionInvoked();
1948 evalJS("myObject.myOverloadedSlot(true)");
1949 QCOMPARE(m_myObject->qtFunctionInvoked(), 25);
1951 // should pick myOverloadedSlot(QDateTime)
1952 m_myObject->resetQtFunctionInvoked();
1953 evalJS("myObject.myOverloadedSlot(new Date())");
1954 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1956 // should pick myOverloadedSlot(QRegExp)
1957 m_myObject->resetQtFunctionInvoked();
1958 evalJS("myObject.myOverloadedSlot(new RegExp())");
1959 QCOMPARE(m_myObject->qtFunctionInvoked(), 34);
1961 // should pick myOverloadedSlot(QVariant)
1963 m_myObject->resetQtFunctionInvoked();
1964 QString f = evalJS("myObject.myOverloadedSlot");
1965 f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao")));
1966 QCOMPARE(m_myObject->qtFunctionInvoked(), 35);
1969 // should pick myOverloadedSlot(QRegExp)
1970 m_myObject->resetQtFunctionInvoked();
1971 evalJS("myObject.myOverloadedSlot(document.body)");
1972 QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=37319", Continue);
1973 QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1975 // should pick myOverloadedSlot(QObject*)
1976 m_myObject->resetQtFunctionInvoked();
1977 evalJS("myObject.myOverloadedSlot(myObject)");
1978 QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1980 // should pick myOverloadedSlot(QObject*)
1981 m_myObject->resetQtFunctionInvoked();
1982 evalJS("myObject.myOverloadedSlot(null)");
1983 QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1985 // should pick myOverloadedSlot(QStringList)
1986 m_myObject->resetQtFunctionInvoked();
1987 evalJS("myObject.myOverloadedSlot(['hello'])");
1988 QCOMPARE(m_myObject->qtFunctionInvoked(), 42);
1991 void tst_QWebFrame::enumerate_data()
1993 QTest::addColumn<QStringList>("expectedNames");
1995 QTest::newRow("enumerate all")
1997 // meta-object-defined properties:
2001 << "p1" << "p2" << "p4" << "p6"
2002 // dynamic properties
2003 << "dp1" << "dp2" << "dp3"
2005 << "destroyed(QObject*)" << "destroyed()"
2007 // not included because it's private:
2008 // << "_q_reregisterTimers(void*)"
2012 << "mySlot()" << "myOtherSlot()");
2015 void tst_QWebFrame::enumerate()
2017 QFETCH(QStringList, expectedNames);
2019 MyEnumTestQObject enumQObject;
2020 // give it some dynamic properties
2021 enumQObject.setProperty("dp1", "dp1");
2022 enumQObject.setProperty("dp2", "dp2");
2023 enumQObject.setProperty("dp3", "dp3");
2024 m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject);
2026 // enumerate in script
2028 evalJS("var enumeratedProperties = []");
2029 evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }");
2030 QStringList result = evalJSV("enumeratedProperties").toStringList();
2031 QCOMPARE(result.size(), expectedNames.size());
2032 for (int i = 0; i < expectedNames.size(); ++i)
2033 QCOMPARE(result.at(i), expectedNames.at(i));
2037 void tst_QWebFrame::objectDeleted()
2039 MyQObject* qobj = new MyQObject();
2040 m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj);
2041 evalJS("bar.objectName = 'foo';");
2042 QCOMPARE(qobj->objectName(), QLatin1String("foo"));
2043 evalJS("bar.intProperty = 123;");
2044 QCOMPARE(qobj->intProperty(), 123);
2045 qobj->resetQtFunctionInvoked();
2046 evalJS("bar.myInvokable.call(bar);");
2047 QCOMPARE(qobj->qtFunctionInvoked(), 0);
2049 // do this, to ensure that we cache that it implements call
2052 // now delete the object
2055 QCOMPARE(evalJS("typeof bar"), sObject);
2057 // any attempt to access properties of the object should result in an exception
2060 QString ret = evalJS("bar.objectName", type);
2061 QCOMPARE(type, sError);
2062 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2066 QString ret = evalJS("bar.objectName = 'foo'", type);
2067 QCOMPARE(type, sError);
2068 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2071 // myInvokable is stored in member table (since we've accessed it before deletion)
2074 evalJS("bar.myInvokable", type);
2075 QCOMPARE(type, sFunction);
2080 QString ret = evalJS("bar.myInvokable.call(bar);", type);
2081 ret = evalJS("bar.myInvokable(bar)", type);
2082 QCOMPARE(type, sError);
2083 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
2085 // myInvokableWithIntArg is not stored in member table (since we've not accessed it)
2088 QString ret = evalJS("bar.myInvokableWithIntArg", type);
2089 QCOMPARE(type, sError);
2090 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2093 // access from script
2094 evalJS("window.o = bar;");
2097 QString ret = evalJS("o.objectName", type);
2098 QCOMPARE(type, sError);
2099 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2103 QString ret = evalJS("o.myInvokable()", type);
2104 QCOMPARE(type, sError);
2105 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
2109 QString ret = evalJS("o.myInvokableWithIntArg(10)", type);
2110 QCOMPARE(type, sError);
2111 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2115 void tst_QWebFrame::typeConversion()
2117 m_myObject->resetQtFunctionInvoked();
2119 QDateTime localdt(QDate(2008,1,18), QTime(12,31,0));
2120 QDateTime utclocaldt = localdt.toUTC();
2121 QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC);
2123 // Dates in JS (default to local)
2124 evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))");
2125 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2126 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2127 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt);
2129 m_myObject->resetQtFunctionInvoked();
2130 evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))");
2131 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2132 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2133 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt);
2135 // Pushing QDateTimes into JS
2137 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}");
2138 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2139 m_myObject->emitMySignalWithDateTimeArg(localdt);
2140 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2141 evalJS("delete window.__date_equals");
2142 m_myObject->emitMySignalWithDateTimeArg(utclocaldt);
2143 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2144 evalJS("delete window.__date_equals");
2145 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2148 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }");
2149 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2150 m_myObject->emitMySignalWithDateTimeArg(utcdt);
2151 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2152 evalJS("delete window.__date_equals");
2153 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2158 class StringListTestObject : public QObject {
2161 QVariant stringList()
2163 return QStringList() << "Q" << "t";
2167 void tst_QWebFrame::arrayObjectEnumerable()
2170 QWebFrame* frame = page.mainFrame();
2171 QObject* qobject = new StringListTestObject();
2172 frame->addToJavaScriptWindowObject("test", qobject, QScriptEngine::ScriptOwnership);
2174 const QString script("var stringArray = test.stringList();"
2176 "for (var i in stringArray) {"
2177 " result += stringArray[i];"
2180 QCOMPARE(frame->evaluateJavaScript(script).toString(), QString::fromLatin1("Qt"));
2183 void tst_QWebFrame::symmetricUrl()
2185 QVERIFY(m_view->url().isEmpty());
2187 QCOMPARE(m_view->history()->count(), 0);
2189 QUrl dataUrl("data:text/html,<h1>Test");
2191 m_view->setUrl(dataUrl);
2192 QCOMPARE(m_view->url(), dataUrl);
2193 QCOMPARE(m_view->history()->count(), 0);
2195 // loading is _not_ immediate, so the text isn't set just yet.
2196 QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty());
2198 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2200 QCOMPARE(m_view->history()->count(), 1);
2201 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test"));
2203 QUrl dataUrl2("data:text/html,<h1>Test2");
2204 QUrl dataUrl3("data:text/html,<h1>Test3");
2206 m_view->setUrl(dataUrl2);
2207 m_view->setUrl(dataUrl3);
2209 QCOMPARE(m_view->url(), dataUrl3);
2211 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2213 QCOMPARE(m_view->history()->count(), 2);
2215 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3"));
2218 void tst_QWebFrame::progressSignal()
2220 QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int)));
2222 QUrl dataUrl("data:text/html,<h1>Test");
2223 m_view->setUrl(dataUrl);
2225 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2227 QVERIFY(progressSpy.size() >= 2);
2229 // WebKit defines initialProgressValue as 10%, not 0%
2230 QCOMPARE(progressSpy.first().first().toInt(), 10);
2232 // But we always end at 100%
2233 QCOMPARE(progressSpy.last().first().toInt(), 100);
2236 void tst_QWebFrame::urlChange()
2238 QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2240 QUrl dataUrl("data:text/html,<h1>Test");
2241 m_view->setUrl(dataUrl);
2243 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2245 QCOMPARE(urlSpy.size(), 1);
2247 QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>");
2248 m_view->setUrl(dataUrl2);
2250 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2252 QCOMPARE(urlSpy.size(), 2);
2256 void tst_QWebFrame::domCycles()
2258 m_view->setHtml("<html><body>");
2259 QVariant v = m_page->mainFrame()->evaluateJavaScript("document");
2260 QVERIFY(v.type() == QVariant::Map);
2263 class FakeReply : public QNetworkReply {
2267 FakeReply(const QNetworkRequest& request, QObject* parent = 0)
2268 : QNetworkReply(parent)
2270 setOperation(QNetworkAccessManager::GetOperation);
2271 setRequest(request);
2272 if (request.url() == QUrl("qrc:/test1.html")) {
2273 setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html"));
2274 setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html"));
2276 #ifndef QT_NO_OPENSSL
2277 else if (request.url() == QUrl("qrc:/fake-ssl-error.html"))
2278 setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error
2280 else if (request.url() == QUrl("http://abcdef.abcdef/"))
2281 setError(QNetworkReply::HostNotFoundError, tr("Invalid URL"));
2283 open(QIODevice::ReadOnly);
2284 QTimer::singleShot(0, this, SLOT(timeout()));
2290 virtual void abort() {}
2291 virtual void close() {}
2294 qint64 readData(char*, qint64)
2302 if (request().url() == QUrl("qrc://test1.html"))
2303 emit error(this->error());
2304 else if (request().url() == QUrl("http://abcdef.abcdef/"))
2305 emit metaDataChanged();
2306 #ifndef QT_NO_OPENSSL
2307 else if (request().url() == QUrl("qrc:/fake-ssl-error.html"))
2316 class FakeNetworkManager : public QNetworkAccessManager {
2320 FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { }
2323 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
2325 QString url = request.url().toString();
2326 if (op == QNetworkAccessManager::GetOperation) {
2327 if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/")
2328 return new FakeReply(request, this);
2329 #ifndef QT_NO_OPENSSL
2330 else if (url == "qrc:/fake-ssl-error.html") {
2331 FakeReply* reply = new FakeReply(request, this);
2332 QList<QSslError> errors;
2333 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError));
2339 return QNetworkAccessManager::createRequest(op, request, outgoingData);
2343 void tst_QWebFrame::requestedUrl()
2346 QWebFrame* frame = page.mainFrame();
2348 // in few seconds, the image should be completely loaded
2349 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2350 FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
2351 page.setNetworkAccessManager(networkManager);
2353 frame->setUrl(QUrl("qrc:/test1.html"));
2354 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2355 QCOMPARE(spy.count(), 1);
2356 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html"));
2357 QCOMPARE(frame->url(), QUrl("qrc:/test2.html"));
2359 frame->setUrl(QUrl("qrc:/non-existent.html"));
2360 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2361 QCOMPARE(spy.count(), 2);
2362 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html"));
2363 QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html"));
2365 frame->setUrl(QUrl("http://abcdef.abcdef"));
2366 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2367 QCOMPARE(spy.count(), 3);
2368 QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/"));
2369 QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/"));
2371 #ifndef QT_NO_OPENSSL
2372 qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
2373 qRegisterMetaType<QNetworkReply* >("QNetworkReply*");
2375 QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
2376 frame->setUrl(QUrl("qrc:/fake-ssl-error.html"));
2377 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2378 QCOMPARE(spy2.count(), 1);
2379 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html"));
2380 QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html"));
2384 void tst_QWebFrame::javaScriptWindowObjectCleared_data()
2386 QTest::addColumn<QString>("html");
2387 QTest::addColumn<int>("signalCount");
2388 QTest::newRow("with <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 1;
2389 QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0;
2392 void tst_QWebFrame::javaScriptWindowObjectCleared()
2395 QWebFrame* frame = page.mainFrame();
2396 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2397 QFETCH(QString, html);
2398 frame->setHtml(html);
2400 QFETCH(int, signalCount);
2401 QCOMPARE(spy.count(), signalCount);
2404 void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate()
2407 QWebFrame* frame = page.mainFrame();
2408 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2409 frame->setHtml("<html></html>");
2410 QCOMPARE(spy.count(), 0);
2411 frame->evaluateJavaScript("var a = 'a';");
2412 QCOMPARE(spy.count(), 1);
2413 // no new clear for a new script:
2414 frame->evaluateJavaScript("var a = 1;");
2415 QCOMPARE(spy.count(), 1);
2418 void tst_QWebFrame::setHtml()
2420 QString html("<html><head></head><body><p>hello world</p></body></html>");
2421 m_view->page()->mainFrame()->setHtml(html);
2422 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html);
2425 void tst_QWebFrame::setHtmlWithResource()
2427 QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>");
2430 QWebFrame* frame = page.mainFrame();
2432 // in few seconds, the image should be completey loaded
2433 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2434 frame->setHtml(html);
2435 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2436 QCOMPARE(spy.count(), 1);
2438 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2439 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2440 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2445 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />"
2448 "<p id='idP'>some text</p>"
2452 // in few seconds, the CSS should be completey loaded
2453 frame->setHtml(html2);
2454 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2455 QCOMPARE(spy.size(), 2);
2457 QWebElement p = frame->documentElement().findAll("p").at(0);
2458 QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red"));
2461 void tst_QWebFrame::setHtmlWithBaseURL()
2463 if (!QDir(TESTS_SOURCE_DIR).exists())
2464 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
2466 QDir::setCurrent(TESTS_SOURCE_DIR);
2468 QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>");
2471 QWebFrame* frame = page.mainFrame();
2473 // in few seconds, the image should be completey loaded
2474 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2476 frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
2477 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2478 QCOMPARE(spy.count(), 1);
2480 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2481 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2482 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2484 // no history item has to be added.
2485 QCOMPARE(m_view->page()->history()->count(), 0);
2488 class TestNetworkManager : public QNetworkAccessManager
2491 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
2493 QList<QUrl> requestedUrls;
2496 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
2497 requestedUrls.append(request.url());
2498 QNetworkRequest redirectedRequest = request;
2499 redirectedRequest.setUrl(QUrl("data:text/html,<p>hello"));
2500 return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData);
2504 void tst_QWebFrame::ipv6HostEncoding()
2506 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2507 m_page->setNetworkAccessManager(networkManager);
2508 networkManager->requestedUrls.clear();
2510 QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html");
2511 m_view->setHtml("<p>Hi", baseUrl);
2512 m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();"
2513 "r.open('GET', 'http://[::1]/test.xml', false);"
2516 QCOMPARE(networkManager->requestedUrls.count(), 1);
2517 QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml"));
2520 void tst_QWebFrame::metaData()
2522 m_view->setHtml("<html>"
2524 " <meta name=\"description\" content=\"Test description\">"
2525 " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">"
2529 QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData();
2531 QCOMPARE(metaData.count(), 2);
2533 QCOMPARE(metaData.value("description"), QString("Test description"));
2534 QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css"));
2535 QCOMPARE(metaData.value("nonexistant"), QString());
2537 m_view->setHtml("<html>"
2539 " <meta name=\"samekey\" content=\"FirstValue\">"
2540 " <meta name=\"samekey\" content=\"SecondValue\">"
2544 metaData = m_view->page()->mainFrame()->metaData();
2546 QCOMPARE(metaData.count(), 2);
2548 QStringList values = metaData.values("samekey");
2549 QCOMPARE(values.count(), 2);
2551 QVERIFY(values.contains("FirstValue"));
2552 QVERIFY(values.contains("SecondValue"));
2554 QCOMPARE(metaData.value("nonexistant"), QString());
2557 #if !defined(Q_WS_MAEMO_5)
2558 void tst_QWebFrame::popupFocus()
2561 view.setHtml("<html>"
2563 " <select name=\"select\">"
2564 " <option>1</option>"
2565 " <option>2</option>"
2567 " <input type=\"text\"> </input>"
2568 " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">"
2569 "This test checks whether showing and hiding a popup"
2570 "takes the focus away from the webpage."
2574 view.resize(400, 100);
2577 QTRY_VERIFY(view.hasFocus());
2579 // open the popup by clicking. check if focus is on the popup
2580 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(25, 25));
2581 QObject* webpopup = firstChildByClassName(&view, "QComboBox");
2582 QComboBox* combo = qobject_cast<QComboBox*>(webpopup);
2583 QVERIFY(combo != 0);
2584 QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup
2586 // hide the popup and check if focus is on the page
2588 QTRY_VERIFY(view.hasFocus() && !combo->view()->hasFocus()); // Focus should be back on the WebView
2592 void tst_QWebFrame::inputFieldFocus()
2595 view.setHtml("<html><body><input type=\"text\"></input></body></html>");
2596 view.resize(400, 100);
2599 QTRY_VERIFY(view.hasFocus());
2601 // double the flashing time, should at least blink once already
2602 int delay = qApp->cursorFlashTime() * 2;
2604 // focus the lineedit and check if it blinks
2605 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(25, 25));
2606 m_inputFieldsTestView = &view;
2607 view.installEventFilter( this );
2608 QTest::qWait(delay);
2609 QVERIFY2(m_inputFieldTestPaintCount >= 3,
2610 "The input field should have a blinking caret");
2613 void tst_QWebFrame::hitTestContent()
2615 QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\">link text</a></body></html>");
2618 QWebFrame* frame = page.mainFrame();
2619 frame->setHtml(html);
2620 page.setViewportSize(QSize(200, 0)); //no height so link is not visible
2621 QWebHitTestResult result = frame->hitTestContent(QPoint(10, 100));
2622 QCOMPARE(result.linkText(), QString("link text"));
2623 QWebElement link = result.linkElement();
2624 QCOMPARE(link.attribute("target"), QString("_foo"));
2627 void tst_QWebFrame::jsByteArray()
2629 QByteArray ba("hello world");
2630 m_myObject->setByteArrayProperty(ba);
2632 // read-only property
2633 QCOMPARE(m_myObject->byteArrayProperty(), ba);
2635 QVariant v = evalJSV("myObject.byteArrayProperty");
2636 QCOMPARE(int(v.type()), int(QVariant::ByteArray));
2638 QCOMPARE(v.toByteArray(), ba);
2641 void tst_QWebFrame::ownership()
2645 QPointer<QObject> ptr = new QObject();
2649 QWebFrame* frame = page.mainFrame();
2650 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership);
2655 QPointer<QObject> ptr = new QObject();
2657 QObject* before = ptr;
2660 QWebFrame* frame = page.mainFrame();
2661 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership);
2663 QVERIFY(ptr == before);
2667 QObject* parent = new QObject();
2668 QObject* child = new QObject(parent);
2670 QWebFrame* frame = page.mainFrame();
2671 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership);
2672 QVariant v = frame->evaluateJavaScript("test");
2673 QCOMPARE(qvariant_cast<QObject*>(v), child);
2675 v = frame->evaluateJavaScript("test");
2676 QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0);
2679 QPointer<QObject> ptr = new QObject();
2683 QWebFrame* frame = page.mainFrame();
2684 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership);
2686 // no parent, so it should be like ScriptOwnership
2690 QObject* parent = new QObject();
2691 QPointer<QObject> child = new QObject(parent);
2692 QVERIFY(child != 0);
2695 QWebFrame* frame = page.mainFrame();
2696 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership);
2698 // has parent, so it should be like QtOwnership
2699 QVERIFY(child != 0);
2704 void tst_QWebFrame::nullValue()
2706 QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null");
2707 QVERIFY(v.isNull());
2710 void tst_QWebFrame::baseUrl_data()
2712 QTest::addColumn<QString>("html");
2713 QTest::addColumn<QUrl>("loadUrl");
2714 QTest::addColumn<QUrl>("url");
2715 QTest::addColumn<QUrl>("baseUrl");
2717 QTest::newRow("null") << QString() << QUrl()
2718 << QUrl("about:blank") << QUrl("about:blank");
2720 QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/")
2721 << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/");
2723 QString html = "<html>"
2725 "<base href=\"http://foobaz.bar/\" />"
2728 QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/")
2729 << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/");
2732 void tst_QWebFrame::baseUrl()
2734 QFETCH(QString, html);
2735 QFETCH(QUrl, loadUrl);
2737 QFETCH(QUrl, baseUrl);
2739 m_page->mainFrame()->setHtml(html, loadUrl);
2740 QCOMPARE(m_page->mainFrame()->url(), url);
2741 QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl);
2744 void tst_QWebFrame::hasSetFocus()
2746 QString html("<html><body><p>top</p>" \
2747 "<iframe width='80%' height='30%'/>" \
2750 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
2751 m_page->mainFrame()->setHtml(html);
2753 waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200);
2754 QCOMPARE(loadSpy.size(), 1);
2756 QList<QWebFrame*> children = m_page->mainFrame()->childFrames();
2757 QWebFrame* frame = children.at(0);
2758 QString innerHtml("<html><body><p>another iframe</p>" \
2759 "<iframe width='80%' height='30%'/>" \
2761 frame->setHtml(innerHtml);
2763 waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2764 QCOMPARE(loadSpy.size(), 2);
2766 m_page->mainFrame()->setFocus();
2767 QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2769 for (int i = 0; i < children.size(); ++i) {
2770 children.at(i)->setFocus();
2771 QTRY_VERIFY(children.at(i)->hasFocus());
2772 QVERIFY(!m_page->mainFrame()->hasFocus());
2775 m_page->mainFrame()->setFocus();
2776 QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2779 void tst_QWebFrame::render()
2781 QString html("<html>" \
2783 "body, iframe { margin: 0px; border: none; }" \
2785 "<body><iframe width='100px' height='100px'/></body>" \
2789 page.mainFrame()->setHtml(html);
2791 QList<QWebFrame*> frames = page.mainFrame()->childFrames();
2792 QWebFrame *frame = frames.at(0);
2793 QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>");
2794 frame->setHtml(innerHtml);
2798 QSize size = page.mainFrame()->contentsSize();
2799 page.setViewportSize(size);
2801 // render contents layer only (the iframe is smaller than the image, so it will have scrollbars)
2802 QPainter painter1(&picture);
2803 frame->render(&painter1, QWebFrame::ContentsLayer);
2806 QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width());
2807 QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height());
2809 // render everything, should be the size of the iframe
2810 QPainter painter2(&picture);
2811 frame->render(&painter2, QWebFrame::AllLayers);
2814 QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px
2815 QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px
2818 void tst_QWebFrame::scrollPosition()
2820 // enlarged image in a small viewport, to provoke the scrollbars to appear
2821 QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>");
2824 page.setViewportSize(QSize(200, 200));
2826 QWebFrame* frame = page.mainFrame();
2827 frame->setHtml(html);
2828 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
2829 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
2831 // try to set the scroll offset programmatically
2832 frame->setScrollPosition(QPoint(23, 29));
2833 QCOMPARE(frame->scrollPosition().x(), 23);
2834 QCOMPARE(frame->scrollPosition().y(), 29);
2836 int x = frame->evaluateJavaScript("window.scrollX").toInt();
2837 int y = frame->evaluateJavaScript("window.scrollY").toInt();
2842 void tst_QWebFrame::scrollToAnchor()
2845 page.setViewportSize(QSize(480, 800));
2846 QWebFrame* frame = page.mainFrame();
2848 QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>"
2849 "<p><a id=\"foo\">This</a> is an anchor</p>"
2850 "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>"
2852 frame->setHtml(html);
2853 frame->setScrollPosition(QPoint(0, 0));
2854 QCOMPARE(frame->scrollPosition().x(), 0);
2855 QCOMPARE(frame->scrollPosition().y(), 0);
2857 QWebElement fooAnchor = frame->findFirstElement("a[id=foo]");
2859 frame->scrollToAnchor("foo");
2860 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top());
2862 frame->scrollToAnchor("bar");
2863 frame->scrollToAnchor("foo");
2864 QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top());
2866 frame->scrollToAnchor("top");
2867 QCOMPARE(frame->scrollPosition().y(), 0);
2869 frame->scrollToAnchor("bar");
2870 frame->scrollToAnchor("notexist");
2871 QVERIFY(frame->scrollPosition().y() != 0);
2875 void tst_QWebFrame::scrollbarsOff()
2878 QWebFrame* mainFrame = view.page()->mainFrame();
2880 mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
2881 mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
2883 QString html("<script>" \
2884 " function checkScrollbar() {" \
2885 " if (innerWidth === document.documentElement.offsetWidth)" \
2886 " document.getElementById('span1').innerText = 'SUCCESS';" \
2888 " document.getElementById('span1').innerText = 'FAIL';" \
2892 " <div style='margin-top:1000px ; margin-left:1000px'>" \
2893 " <a id='offscreen' href='a'>End</a>" \
2895 "<span id='span1'></span>" \
2900 ::waitForSignal(&view, SIGNAL(loadFinished(bool)));
2902 mainFrame->evaluateJavaScript("checkScrollbar();");
2903 QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS"));
2906 void tst_QWebFrame::evaluateWillCauseRepaint()
2909 QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">"
2910 "junk</div>bottom</body></html>");
2914 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
2915 QTest::qWaitForWindowShown(&view);
2920 view.page()->mainFrame()->evaluateJavaScript(
2921 "document.getElementById('junk').style.display = 'none';");
2923 ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect)));
2926 class TestFactory : public QObject
2931 : obj(0), counter(0)
2934 Q_INVOKABLE QObject* getNewObject()
2937 obj = new QObject(this);
2938 obj->setObjectName(QLatin1String("test") + QString::number(++counter));
2947 void tst_QWebFrame::qObjectWrapperWithSameIdentity()
2949 m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>"
2950 "<body><span id='span1'>test</span></body>");
2952 QWebFrame* mainFrame = m_view->page()->mainFrame();
2953 QCOMPARE(mainFrame->toPlainText(), QString("test"));
2955 mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership);
2957 mainFrame->evaluateJavaScript("triggerBug();");
2958 QCOMPARE(mainFrame->toPlainText(), QString("test1"));
2960 mainFrame->evaluateJavaScript("triggerBug();");
2961 QCOMPARE(mainFrame->toPlainText(), QString("test2"));
2964 void tst_QWebFrame::introspectQtMethods_data()
2966 QTest::addColumn<QString>("objectExpression");
2967 QTest::addColumn<QString>("methodName");
2968 QTest::addColumn<QStringList>("expectedPropertyNames");
2970 QTest::newRow("myObject.mySignal")
2971 << "myObject" << "mySignal" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2972 QTest::newRow("myObject.mySlot")
2973 << "myObject" << "mySlot" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2974 QTest::newRow("myObject.myInvokable")
2975 << "myObject" << "myInvokable" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2976 QTest::newRow("myObject.mySignal.connect")
2977 << "myObject.mySignal" << "connect" << (QStringList() << "length" << "name");
2978 QTest::newRow("myObject.mySignal.disconnect")
2979 << "myObject.mySignal" << "disconnect" << (QStringList() << "length" << "name");
2982 void tst_QWebFrame::introspectQtMethods()
2984 QFETCH(QString, objectExpression);
2985 QFETCH(QString, methodName);
2986 QFETCH(QStringList, expectedPropertyNames);
2988 QString methodLookup = QString::fromLatin1("%0['%1']").arg(objectExpression).arg(methodName);
2989 QCOMPARE(evalJSV(QString::fromLatin1("Object.getOwnPropertyNames(%0).sort()").arg(methodLookup)).toStringList(), expectedPropertyNames);
2991 for (int i = 0; i < expectedPropertyNames.size(); ++i) {
2992 QString name = expectedPropertyNames.at(i);
2993 QCOMPARE(evalJS(QString::fromLatin1("%0.hasOwnProperty('%1')").arg(methodLookup).arg(name)), sTrue);
2994 evalJS(QString::fromLatin1("var descriptor = Object.getOwnPropertyDescriptor(%0, '%1')").arg(methodLookup).arg(name));
2995 QCOMPARE(evalJS("typeof descriptor"), QString::fromLatin1("object"));
2996 QCOMPARE(evalJS("descriptor.get"), sUndefined);
2997 QCOMPARE(evalJS("descriptor.set"), sUndefined);
2998 QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue);
2999 QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse);
3000 QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse);
3003 QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty());
3006 QTEST_MAIN(tst_QWebFrame)
3007 #include "tst_qwebframe.moc"