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>
41 #if defined(Q_OS_SYMBIAN)
50 * Starts an event loop that runs until the given signal is received.
51 Optionally the event loop
52 * can return earlier on a timeout.
54 * \return \p true if the requested signal was received
57 static bool waitForSignal(QObject* obj, const char* signal, int timeout = 0)
60 QObject::connect(obj, signal, &loop, SLOT(quit()));
62 QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
64 QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
65 timer.setSingleShot(true);
69 return timeoutSpy.isEmpty();
72 /* Mostly a test for the JavaScript related parts of QWebFrame */
78 Q_DECLARE_METATYPE(CustomType)
80 Q_DECLARE_METATYPE(QBrush*)
81 Q_DECLARE_METATYPE(QObjectList)
82 Q_DECLARE_METATYPE(QList<int>)
83 Q_DECLARE_METATYPE(Qt::BrushStyle)
84 Q_DECLARE_METATYPE(QVariantList)
85 Q_DECLARE_METATYPE(QVariantMap)
87 class MyQObject : public QObject
91 Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty)
92 Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty)
93 Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty)
94 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
95 Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty)
96 Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty)
97 Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty)
98 Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false)
99 Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty)
100 Q_PROPERTY(int readOnlyProperty READ readOnlyProperty)
101 Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
102 Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType)
103 Q_ENUMS(Policy Strategy)
124 AllAbility = FooAbility | BarAbility | BazAbility
127 Q_DECLARE_FLAGS(Ability, AbilityFlag)
129 MyQObject(QObject* parent = 0)
132 m_variantValue(QLatin1String("foo")),
133 m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))),
134 m_stringValue(QLatin1String("bar")),
135 m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")),
136 m_brushValue(QColor(10, 20, 30, 40)),
137 m_hiddenValue(456.0),
138 m_writeOnlyValue(789),
139 m_readOnlyValue(987),
140 m_qtFunctionInvoked(-1) { }
144 int intProperty() const {
147 void setIntProperty(int value) {
151 QVariant variantProperty() const {
152 return m_variantValue;
154 void setVariantProperty(const QVariant &value) {
155 m_variantValue = value;
158 QVariantList variantListProperty() const {
159 return m_variantListValue;
161 void setVariantListProperty(const QVariantList &value) {
162 m_variantListValue = value;
165 QString stringProperty() const {
166 return m_stringValue;
168 void setStringProperty(const QString &value) {
169 m_stringValue = value;
172 QStringList stringListProperty() const {
173 return m_stringListValue;
175 void setStringListProperty(const QStringList &value) {
176 m_stringListValue = value;
179 QByteArray byteArrayProperty() const {
180 return m_byteArrayValue;
182 void setByteArrayProperty(const QByteArray &value) {
183 m_byteArrayValue = value;
186 QBrush brushProperty() const {
189 Q_INVOKABLE void setBrushProperty(const QBrush &value) {
190 m_brushValue = value;
193 double hiddenProperty() const {
194 return m_hiddenValue;
196 void setHiddenProperty(double value) {
197 m_hiddenValue = value;
200 int writeOnlyProperty() const {
201 return m_writeOnlyValue;
203 void setWriteOnlyProperty(int value) {
204 m_writeOnlyValue = value;
207 int readOnlyProperty() const {
208 return m_readOnlyValue;
211 QKeySequence shortcut() const {
214 void setShortcut(const QKeySequence &seq) {
218 CustomType propWithCustomType() const {
221 void setPropWithCustomType(const CustomType &c) {
225 int qtFunctionInvoked() const {
226 return m_qtFunctionInvoked;
229 QVariantList qtFunctionActuals() const {
233 void resetQtFunctionInvoked() {
234 m_qtFunctionInvoked = -1;
238 Q_INVOKABLE void myInvokable() {
239 m_qtFunctionInvoked = 0;
241 Q_INVOKABLE void myInvokableWithIntArg(int arg) {
242 m_qtFunctionInvoked = 1;
245 Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) {
246 m_qtFunctionInvoked = 2;
249 Q_INVOKABLE void myInvokableWithFloatArg(float arg) {
250 m_qtFunctionInvoked = 3;
253 Q_INVOKABLE void myInvokableWithDoubleArg(double arg) {
254 m_qtFunctionInvoked = 4;
257 Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) {
258 m_qtFunctionInvoked = 5;
261 Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) {
262 m_qtFunctionInvoked = 6;
263 m_actuals << arg1 << arg2;
265 Q_INVOKABLE int myInvokableReturningInt() {
266 m_qtFunctionInvoked = 7;
269 Q_INVOKABLE qlonglong myInvokableReturningLongLong() {
270 m_qtFunctionInvoked = 39;
273 Q_INVOKABLE QString myInvokableReturningString() {
274 m_qtFunctionInvoked = 8;
275 return QLatin1String("ciao");
277 Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload
278 m_qtFunctionInvoked = 9;
279 m_actuals << arg1 << arg2;
281 Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) {
282 m_qtFunctionInvoked = 10;
285 Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) {
286 m_qtFunctionInvoked = 36;
289 Q_INVOKABLE Policy myInvokableReturningEnum() {
290 m_qtFunctionInvoked = 37;
293 Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() {
294 m_qtFunctionInvoked = 38;
297 Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() {
298 m_qtFunctionInvoked = 11;
299 return QVector<int>();
301 Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) {
302 m_qtFunctionInvoked = 12;
304 Q_INVOKABLE QObject* myInvokableReturningQObjectStar() {
305 m_qtFunctionInvoked = 13;
308 Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) {
309 m_qtFunctionInvoked = 14;
310 m_actuals << qVariantFromValue(lst);
313 Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) {
314 m_qtFunctionInvoked = 15;
318 Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) {
319 m_qtFunctionInvoked = 16;
323 Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) {
324 m_qtFunctionInvoked = 17;
325 m_actuals << qVariantFromValue(lst);
328 Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) {
329 m_qtFunctionInvoked = 18;
330 m_actuals << qVariantFromValue(obj);
333 Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) {
334 m_qtFunctionInvoked = 19;
335 m_actuals << qVariantFromValue(brush);
338 Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) {
339 m_qtFunctionInvoked = 43;
340 m_actuals << qVariantFromValue(style);
342 Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) {
343 m_qtFunctionInvoked = 44;
344 m_actuals << qVariantFromValue(arg);
346 Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) {
347 m_qtFunctionInvoked = 45;
348 m_actuals << qVariantFromValue(arg);
350 Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) {
351 m_qtFunctionInvoked = 46;
352 m_actuals << qVariantFromValue(arg);
354 Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") {
355 m_qtFunctionInvoked = 47;
356 m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2);
358 Q_INVOKABLE QObject& myInvokableReturningRef() {
359 m_qtFunctionInvoked = 48;
362 Q_INVOKABLE const QObject& myInvokableReturningConstRef() const {
363 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49;
366 Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) {
367 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50;
368 m_actuals << qVariantFromValue(arg);
370 Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) {
371 const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51;
372 m_actuals << qVariantFromValue(arg);
374 Q_INVOKABLE void myInvokableWithBoolArg(bool arg) {
375 m_qtFunctionInvoked = 52;
379 void emitMySignal() {
382 void emitMySignalWithIntArg(int arg) {
383 emit mySignalWithIntArg(arg);
385 void emitMySignal2(bool arg) {
388 void emitMySignal2() {
391 void emitMySignalWithDateTimeArg(QDateTime dt) {
392 emit mySignalWithDateTimeArg(dt);
394 void emitMySignalWithRegexArg(QRegExp r) {
395 emit mySignalWithRegexArg(r);
400 m_qtFunctionInvoked = 20;
402 void mySlotWithIntArg(int arg) {
403 m_qtFunctionInvoked = 21;
406 void mySlotWithDoubleArg(double arg) {
407 m_qtFunctionInvoked = 22;
410 void mySlotWithStringArg(const QString &arg) {
411 m_qtFunctionInvoked = 23;
415 void myOverloadedSlot() {
416 m_qtFunctionInvoked = 24;
418 void myOverloadedSlot(QObject* arg) {
419 m_qtFunctionInvoked = 41;
420 m_actuals << qVariantFromValue(arg);
422 void myOverloadedSlot(bool arg) {
423 m_qtFunctionInvoked = 25;
426 void myOverloadedSlot(const QStringList &arg) {
427 m_qtFunctionInvoked = 42;
430 void myOverloadedSlot(double arg) {
431 m_qtFunctionInvoked = 26;
434 void myOverloadedSlot(float arg) {
435 m_qtFunctionInvoked = 27;
438 void myOverloadedSlot(int arg) {
439 m_qtFunctionInvoked = 28;
442 void myOverloadedSlot(const QString &arg) {
443 m_qtFunctionInvoked = 29;
446 void myOverloadedSlot(const QColor &arg) {
447 m_qtFunctionInvoked = 30;
450 void myOverloadedSlot(const QBrush &arg) {
451 m_qtFunctionInvoked = 31;
454 void myOverloadedSlot(const QDateTime &arg) {
455 m_qtFunctionInvoked = 32;
458 void myOverloadedSlot(const QDate &arg) {
459 m_qtFunctionInvoked = 33;
462 void myOverloadedSlot(const QRegExp &arg) {
463 m_qtFunctionInvoked = 34;
466 void myOverloadedSlot(const QVariant &arg) {
467 m_qtFunctionInvoked = 35;
471 void qscript_call(int arg) {
472 m_qtFunctionInvoked = 40;
477 void myProtectedSlot() {
478 m_qtFunctionInvoked = 36;
482 void myPrivateSlot() { }
486 void mySignalWithIntArg(int arg);
487 void mySignalWithDoubleArg(double arg);
488 void mySignal2(bool arg = false);
489 void mySignalWithDateTimeArg(QDateTime dt);
490 void mySignalWithRegexArg(QRegExp r);
494 QVariant m_variantValue;
495 QVariantList m_variantListValue;
496 QString m_stringValue;
497 QStringList m_stringListValue;
498 QByteArray m_byteArrayValue;
500 double m_hiddenValue;
501 int m_writeOnlyValue;
503 QKeySequence m_shortcut;
504 CustomType m_customType;
505 int m_qtFunctionInvoked;
506 QVariantList m_actuals;
509 class MyOtherQObject : public MyQObject
512 MyOtherQObject(QObject* parent = 0)
513 : MyQObject(parent) { }
516 class MyEnumTestQObject : public QObject
519 Q_PROPERTY(QString p1 READ p1)
520 Q_PROPERTY(QString p2 READ p2)
521 Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false)
522 Q_PROPERTY(QString p4 READ p4)
523 Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false)
524 Q_PROPERTY(QString p6 READ p6)
526 MyEnumTestQObject(QObject* parent = 0)
527 : QObject(parent) { }
529 return QLatin1String("p1");
532 return QLatin1String("p2");
535 return QLatin1String("p3");
538 return QLatin1String("p4");
541 return QLatin1String("p5");
544 return QLatin1String("p5");
548 void myOtherSlot() { }
553 class tst_QWebFrame : public QObject
559 virtual ~tst_QWebFrame();
560 bool eventFilter(QObject* watched, QEvent* event);
567 void getSetStaticProperty();
568 void getSetDynamicProperty();
569 void getSetChildren();
570 void callQtInvokable();
571 void connectAndDisconnect();
573 void classConstructor();
574 void overrideInvokable();
575 void transferInvokable();
578 void overloadedSlots();
579 void enumerate_data();
581 void objectDeleted();
582 void typeConversion();
584 void progressSignal();
588 void javaScriptWindowObjectCleared_data();
589 void javaScriptWindowObjectCleared();
590 void javaScriptWindowObjectClearedOnEvaluate();
592 void setHtmlWithResource();
593 void setHtmlWithBaseURL();
594 void ipv6HostEncoding();
597 void hitTestContent();
605 void scrollPosition();
606 void evaluateWillCauseRepaint();
607 void qObjectWrapperWithSameIdentity();
610 QString evalJS(const QString&s) {
611 // Convert an undefined return variant to the string "undefined"
612 QVariant ret = evalJSV(s);
613 if (ret.userType() == QMetaType::Void)
616 return ret.toString();
618 QVariant evalJSV(const QString &s) {
619 return m_page->mainFrame()->evaluateJavaScript(s);
622 QString evalJS(const QString&s, QString& type) {
623 return evalJSV(s, type).toString();
625 QVariant evalJSV(const QString &s, QString& type) {
626 // As a special measure, if we get an exception we set the type to 'error'
627 // (in ecma, an Error object has typeof object, but qtscript has a convenience function)
628 // Similarly, an array is an object, but we'd prefer to have a type of 'array'
629 // Also, consider a QMetaType::Void QVariant to be undefined
631 escaped.replace('\'', "\\'"); // Don't preescape your single quotes!
632 evalJS("var retvalue;\
635 retvalue = eval('" + escaped + "'); \
636 typevalue = typeof retvalue; \
637 if (retvalue instanceof Array) \
638 typevalue = 'array'; \
641 retvalue = e.name + ': ' + e.message;\
642 typevalue = 'error';\
644 QVariant ret = evalJSV("retvalue");
645 if (ret.userType() != QMetaType::Void)
646 type = evalJS("typevalue");
648 ret = QString("undefined");
651 evalJS("delete retvalue; delete typevalue");
654 QObject* firstChildByClassName(QObject* parent, const char* className) {
655 const QObjectList & children = parent->children();
656 foreach (QObject* child, children) {
657 if (!strcmp(child->metaObject()->className(), className)) {
665 const QString sFalse;
666 const QString sUndefined;
667 const QString sArray;
668 const QString sFunction;
669 const QString sError;
670 const QString sString;
671 const QString sObject;
672 const QString sNumber;
677 MyQObject* m_myObject;
678 QWebView* m_popupTestView;
679 int m_popupTestPaintCount;
682 tst_QWebFrame::tst_QWebFrame()
683 : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"),
684 sString("string"), sObject("object"), sNumber("number"), m_popupTestView(0), m_popupTestPaintCount(0)
688 tst_QWebFrame::~tst_QWebFrame()
692 bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event)
694 // used on the popupFocus test
695 if (watched == m_popupTestView) {
696 if (event->type() == QEvent::Paint)
697 m_popupTestPaintCount++;
699 return QObject::eventFilter(watched, event);
702 void tst_QWebFrame::init()
704 m_view = new QWebView();
705 m_page = m_view->page();
706 m_myObject = new MyQObject();
707 m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject);
708 QDir::setCurrent(SRCDIR);
711 void tst_QWebFrame::cleanup()
717 void tst_QWebFrame::getSetStaticProperty()
719 QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined);
721 // initial value (set in MyQObject constructor)
724 QVariant ret = evalJSV("myObject.intProperty", type);
725 QCOMPARE(type, sNumber);
726 QCOMPARE(ret.type(), QVariant::Double);
727 QCOMPARE(ret.toInt(), 123);
729 QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue);
733 QVariant ret = evalJSV("myObject.variantProperty", type);
734 QCOMPARE(type, sString);
735 QCOMPARE(ret.type(), QVariant::String);
736 QCOMPARE(ret.toString(), QLatin1String("foo"));
738 QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue);
742 QVariant ret = evalJSV("myObject.stringProperty", type);
743 QCOMPARE(type, sString);
744 QCOMPARE(ret.type(), QVariant::String);
745 QCOMPARE(ret.toString(), QLatin1String("bar"));
747 QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue);
751 QVariant ret = evalJSV("myObject.variantListProperty", type);
752 QCOMPARE(type, sArray);
753 QCOMPARE(ret.type(), QVariant::List);
754 QVariantList vl = ret.value<QVariantList>();
755 QCOMPARE(vl.size(), 2);
756 QCOMPARE(vl.at(0).toInt(), 123);
757 QCOMPARE(vl.at(1).toString(), QLatin1String("foo"));
759 QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue);
760 QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue);
761 QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue);
765 QVariant ret = evalJSV("myObject.stringListProperty", type);
766 QCOMPARE(type, sArray);
767 QCOMPARE(ret.type(), QVariant::List);
768 QVariantList vl = ret.value<QVariantList>();
769 QCOMPARE(vl.size(), 2);
770 QCOMPARE(vl.at(0).toString(), QLatin1String("zig"));
771 QCOMPARE(vl.at(1).toString(), QLatin1String("zag"));
773 QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue);
774 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
775 QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig"));
776 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
777 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag"));
779 // property change in C++ should be reflected in script
780 m_myObject->setIntProperty(456);
781 QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue);
782 m_myObject->setIntProperty(789);
783 QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue);
785 m_myObject->setVariantProperty(QLatin1String("bar"));
786 QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue);
787 m_myObject->setVariantProperty(42);
788 QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue);
789 m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
791 // QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant);
793 m_myObject->setStringProperty(QLatin1String("baz"));
794 QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue);
795 m_myObject->setStringProperty(QLatin1String("zab"));
796 QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue);
798 // property change in script should be reflected in C++
799 QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123"));
800 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
801 QCOMPARE(m_myObject->intProperty(), 123);
802 QCOMPARE(evalJS("myObject.intProperty = 'ciao!';"
803 "myObject.intProperty == 0"), sTrue);
804 QCOMPARE(m_myObject->intProperty(), 0);
805 QCOMPARE(evalJS("myObject.intProperty = '123';"
806 "myObject.intProperty == 123"), sTrue);
807 QCOMPARE(m_myObject->intProperty(), 123);
809 QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao"));
810 QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao"));
811 QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao"));
812 QCOMPARE(evalJS("myObject.stringProperty = 123;"
813 "myObject.stringProperty"), QLatin1String("123"));
814 QCOMPARE(m_myObject->stringProperty(), QLatin1String("123"));
815 QCOMPARE(evalJS("myObject.stringProperty = null"), QString());
816 QCOMPARE(evalJS("myObject.stringProperty"), QString());
817 QCOMPARE(m_myObject->stringProperty(), QString());
818 QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined);
819 QCOMPARE(evalJS("myObject.stringProperty"), QString());
820 QCOMPARE(m_myObject->stringProperty(), QString());
822 QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);"
823 "myObject.variantProperty").toDouble(), 1234.0);
824 QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0);
826 QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);"
827 "myObject.variantProperty"), sTrue);
828 QCOMPARE(m_myObject->variantProperty().toBool(), true);
830 QCOMPARE(evalJS("myObject.variantProperty = null;"
831 "myObject.variantProperty.valueOf()"), sUndefined);
832 QCOMPARE(m_myObject->variantProperty(), QVariant());
833 QCOMPARE(evalJS("myObject.variantProperty = undefined;"
834 "myObject.variantProperty.valueOf()"), sUndefined);
835 QCOMPARE(m_myObject->variantProperty(), QVariant());
837 QCOMPARE(evalJS("myObject.variantProperty = 'foo';"
838 "myObject.variantProperty.valueOf()"), QLatin1String("foo"));
839 QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo")));
840 QCOMPARE(evalJS("myObject.variantProperty = 42;"
841 "myObject.variantProperty").toDouble(), 42.0);
842 QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0);
844 QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];"
845 "myObject.variantListProperty.length == 3"), sTrue);
846 QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue);
847 QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two"));
848 QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue);
850 QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];"
851 "myObject.stringListProperty.length == 3"), sTrue);
852 QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
853 QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue);
854 QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
855 QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two"));
856 QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString);
857 QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true"));
860 QCOMPARE(evalJS("delete myObject.intProperty"), sFalse);
861 QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
863 QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse);
864 QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0);
867 QCOMPARE(evalJS("myObject.customProperty"), sUndefined);
868 QCOMPARE(evalJS("myObject.customProperty = 123;"
869 "myObject.customProperty == 123"), sTrue);
870 QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty");
871 QCOMPARE(v.type(), QVariant::Double);
872 QCOMPARE(v.toInt(), 123);
874 // non-scriptable property
875 QCOMPARE(m_myObject->hiddenProperty(), 456.0);
876 QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined);
877 QCOMPARE(evalJS("myObject.hiddenProperty = 123;"
878 "myObject.hiddenProperty == 123"), sTrue);
879 QCOMPARE(m_myObject->hiddenProperty(), 456.0);
881 // write-only property
882 QCOMPARE(m_myObject->writeOnlyProperty(), 789);
883 QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined);
884 QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;"
885 "typeof myObject.writeOnlyProperty"), sUndefined);
886 QCOMPARE(m_myObject->writeOnlyProperty(), 123);
888 // read-only property
889 QCOMPARE(m_myObject->readOnlyProperty(), 987);
890 QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue);
891 QCOMPARE(evalJS("myObject.readOnlyProperty = 654;"
892 "myObject.readOnlyProperty == 987"), sTrue);
893 QCOMPARE(m_myObject->readOnlyProperty(), 987);
896 void tst_QWebFrame::getSetDynamicProperty()
898 // initially the object does not have the property
899 // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty
901 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
902 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
904 // add a dynamic property in C++
905 QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
906 //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue);
907 QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue);
908 QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue);
910 // property change in script should be reflected in C++
911 QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';"
912 "myObject.dynamicProperty"), QLatin1String("foo"));
913 QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo"));
915 // delete the property (XFAIL - can't delete properties)
916 QEXPECT_FAIL("", "can't delete properties", Continue);
917 QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue);
919 QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false);
920 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
921 // QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
922 QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
926 void tst_QWebFrame::getSetChildren()
928 // initially the object does not have the child
929 // (again, no hasOwnProperty)
931 //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
932 QCOMPARE(evalJS("typeof myObject.child"), sUndefined);
935 MyQObject* child = new MyQObject(m_myObject);
936 child->setObjectName("child");
937 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue);
938 QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue);
941 MyQObject* grandChild = new MyQObject(child);
942 grandChild->setObjectName("grandChild");
943 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue);
944 QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue);
948 // QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse);
949 QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue);
953 // QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
954 QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue);
957 Q_DECLARE_METATYPE(QVector<int>)
958 Q_DECLARE_METATYPE(QVector<double>)
959 Q_DECLARE_METATYPE(QVector<QString>)
961 void tst_QWebFrame::callQtInvokable()
963 qRegisterMetaType<QObjectList>();
965 m_myObject->resetQtFunctionInvoked();
966 QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined);
967 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
968 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
970 // extra arguments should silently be ignored
971 m_myObject->resetQtFunctionInvoked();
972 QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined);
973 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
974 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
976 m_myObject->resetQtFunctionInvoked();
977 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined);
978 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
979 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
980 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
982 m_myObject->resetQtFunctionInvoked();
983 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined);
984 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
985 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
986 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
988 m_myObject->resetQtFunctionInvoked();
989 QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined);
990 QCOMPARE(m_myObject->qtFunctionInvoked(), 2);
991 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
992 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123));
994 m_myObject->resetQtFunctionInvoked();
995 QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined);
996 QCOMPARE(m_myObject->qtFunctionInvoked(), 3);
997 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
998 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
1000 m_myObject->resetQtFunctionInvoked();
1001 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined);
1002 QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
1003 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1004 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
1006 m_myObject->resetQtFunctionInvoked();
1007 QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined);
1008 QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
1009 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1010 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5);
1012 m_myObject->resetQtFunctionInvoked();
1013 QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined);
1014 QCOMPARE(m_myObject->qtFunctionInvoked(), 52);
1015 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1016 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true);
1018 m_myObject->resetQtFunctionInvoked();
1019 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined);
1020 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1021 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1022 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao"));
1024 m_myObject->resetQtFunctionInvoked();
1025 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined);
1026 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1027 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1028 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1030 m_myObject->resetQtFunctionInvoked();
1031 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined);
1032 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1033 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1034 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1035 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1037 m_myObject->resetQtFunctionInvoked();
1038 QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined);
1039 QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1040 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1041 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1042 QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1044 m_myObject->resetQtFunctionInvoked();
1045 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined);
1046 QCOMPARE(m_myObject->qtFunctionInvoked(), 6);
1047 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1048 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1049 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1051 m_myObject->resetQtFunctionInvoked();
1052 QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123"));
1053 QCOMPARE(m_myObject->qtFunctionInvoked(), 7);
1054 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1056 m_myObject->resetQtFunctionInvoked();
1057 QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456"));
1058 QCOMPARE(m_myObject->qtFunctionInvoked(), 39);
1059 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1061 m_myObject->resetQtFunctionInvoked();
1062 QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao"));
1063 QCOMPARE(m_myObject->qtFunctionInvoked(), 8);
1064 QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1066 m_myObject->resetQtFunctionInvoked();
1067 QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined);
1068 QCOMPARE(m_myObject->qtFunctionInvoked(), 9);
1069 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1070 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1071 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1073 m_myObject->resetQtFunctionInvoked();
1074 QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined);
1075 QCOMPARE(m_myObject->qtFunctionInvoked(), 44);
1076 m_myObject->resetQtFunctionInvoked();
1079 QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type);
1080 QCOMPARE(type, sError);
1081 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n myInvokableWithVoidStarArg(void*)"));
1082 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1085 m_myObject->resetQtFunctionInvoked();
1088 QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type);
1089 QCOMPARE(type, sError);
1090 QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)"));
1093 m_myObject->resetQtFunctionInvoked();
1096 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type);
1097 QCOMPARE(type, sUndefined);
1098 QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1099 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1100 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1101 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello"));
1104 m_myObject->resetQtFunctionInvoked();
1107 QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type);
1108 QCOMPARE(type, sUndefined);
1109 QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1110 QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1111 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1112 QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString());
1115 // calling function that returns (const)ref
1116 m_myObject->resetQtFunctionInvoked();
1119 QString ret = evalJS("typeof myObject.myInvokableReturningRef()");
1120 QCOMPARE(ret, sUndefined);
1121 //QVERIFY(!m_engine->hasUncaughtException());
1122 QCOMPARE(m_myObject->qtFunctionInvoked(), 48);
1125 m_myObject->resetQtFunctionInvoked();
1128 QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()");
1129 QCOMPARE(ret, sUndefined);
1130 //QVERIFY(!m_engine->hasUncaughtException());
1131 QCOMPARE(m_myObject->qtFunctionInvoked(), 49);
1134 m_myObject->resetQtFunctionInvoked();
1137 QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type);
1138 QCOMPARE(m_myObject->qtFunctionInvoked(), 13);
1139 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1140 QCOMPARE(type, sObject);
1141 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1144 m_myObject->resetQtFunctionInvoked();
1147 QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type);
1148 QCOMPARE(m_myObject->qtFunctionInvoked(), 14);
1149 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1150 QCOMPARE(type, sArray);
1151 QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList
1152 QVariantList vl = qvariant_cast<QVariantList>(ret);
1153 QCOMPARE(vl.count(), 1);
1156 m_myObject->resetQtFunctionInvoked();
1159 m_myObject->setVariantProperty(QVariant(123));
1160 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type);
1161 QCOMPARE(type, sNumber);
1162 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1163 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1164 QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty());
1165 QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int
1166 QCOMPARE(ret.toInt(),123);
1169 m_myObject->resetQtFunctionInvoked();
1172 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type);
1173 QCOMPARE(type, sObject);
1174 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1175 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1176 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1177 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1180 m_myObject->resetQtFunctionInvoked();
1183 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type);
1184 QCOMPARE(type, sObject);
1185 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1186 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1187 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1188 QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1191 /* XFAIL - variant support
1192 m_myObject->resetQtFunctionInvoked();
1194 m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
1195 QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
1196 QVERIFY(ret.isVariant());
1197 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1198 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1199 QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0));
1200 QCOMPARE(ret.toVariant(), m_myObject->variantProperty());
1204 m_myObject->resetQtFunctionInvoked();
1207 QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type);
1208 QCOMPARE(type, sNumber);
1209 QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1210 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1211 QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123));
1212 QCOMPARE(ret.userType(), int(QMetaType::Double));
1213 QCOMPARE(ret.toInt(),123);
1216 m_myObject->resetQtFunctionInvoked();
1219 QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type);
1220 QCOMPARE(m_myObject->qtFunctionInvoked(), 16);
1221 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1223 QVariant v = m_myObject->qtFunctionActuals().at(0);
1224 QCOMPARE(v.userType(), int(QMetaType::QVariantMap));
1226 QVariantMap vmap = qvariant_cast<QVariantMap>(v);
1227 QCOMPARE(vmap.keys().size(), 2);
1228 QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1229 QCOMPARE(vmap.value("a"), QVariant(123));
1230 QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1231 QCOMPARE(vmap.value("b"), QVariant("ciao"));
1233 QCOMPARE(type, sObject);
1235 QCOMPARE(ret.userType(), int(QMetaType::QVariantMap));
1236 vmap = qvariant_cast<QVariantMap>(ret);
1237 QCOMPARE(vmap.keys().size(), 2);
1238 QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1239 QCOMPARE(vmap.value("a"), QVariant(123));
1240 QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1241 QCOMPARE(vmap.value("b"), QVariant("ciao"));
1244 m_myObject->resetQtFunctionInvoked();
1247 QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type);
1248 QCOMPARE(m_myObject->qtFunctionInvoked(), 17);
1249 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1250 QVariant v = m_myObject->qtFunctionActuals().at(0);
1251 QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
1252 QList<int> ilst = qvariant_cast<QList<int> >(v);
1253 QCOMPARE(ilst.size(), 2);
1254 QCOMPARE(ilst.at(0), 1);
1255 QCOMPARE(ilst.at(1), 5);
1257 QCOMPARE(type, sArray);
1258 QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist
1259 QVariantList vlst = qvariant_cast<QVariantList>(ret);
1260 QCOMPARE(vlst.size(), 2);
1261 QCOMPARE(vlst.at(0).toInt(), 1);
1262 QCOMPARE(vlst.at(1).toInt(), 5);
1265 m_myObject->resetQtFunctionInvoked();
1268 QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type);
1269 QCOMPARE(m_myObject->qtFunctionInvoked(), 18);
1270 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1271 QVariant v = m_myObject->qtFunctionActuals().at(0);
1272 QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1273 QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject);
1275 QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1276 QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject);
1278 QCOMPARE(type, sObject);
1281 m_myObject->resetQtFunctionInvoked();
1283 // no implicit conversion from integer to QObject*
1285 evalJS("myObject.myInvokableWithQObjectStarArg(123)", type);
1286 QCOMPARE(type, sError);
1290 m_myObject->resetQtFunctionInvoked();
1292 QString fun = evalJS("myObject.myInvokableWithQBrushArg");
1293 Q_ASSERT(fun.isFunction());
1294 QColor color(10, 20, 30, 40);
1295 // QColor should be converted to a QBrush
1296 QVariant ret = fun.call(QString(), QStringList()
1297 << qScriptValueFromValue(m_engine, color));
1298 QCOMPARE(m_myObject->qtFunctionInvoked(), 19);
1299 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1300 QVariant v = m_myObject->qtFunctionActuals().at(0);
1301 QCOMPARE(v.userType(), int(QMetaType::QBrush));
1302 QCOMPARE(qvariant_cast<QColor>(v), color);
1304 QCOMPARE(qscriptvalue_cast<QColor>(ret), color);
1308 // private slots should not be part of the QObject binding
1309 QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined);
1311 // protected slots should be fine
1312 m_myObject->resetQtFunctionInvoked();
1313 evalJS("myObject.myProtectedSlot()");
1314 QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1316 // call with too few arguments
1319 QString ret = evalJS("myObject.myInvokableWithIntArg()", type);
1320 QCOMPARE(type, sError);
1321 QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)"));
1324 // call function where not all types have been registered
1325 m_myObject->resetQtFunctionInvoked();
1328 QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type);
1329 QCOMPARE(type, sError);
1330 QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'"));
1331 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1334 // call function with incompatible argument type
1335 m_myObject->resetQtFunctionInvoked();
1338 QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type);
1339 QCOMPARE(type, sError);
1340 QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)"));
1341 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1345 void tst_QWebFrame::connectAndDisconnect()
1347 // connect(function)
1348 QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction);
1349 QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction);
1350 QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction);
1354 evalJS("myObject.mySignal.connect(123)", type);
1355 QCOMPARE(type, sError);
1358 evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }");
1360 QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined);
1362 evalJS("gotSignal = false");
1363 evalJS("myObject.mySignal()");
1364 QCOMPARE(evalJS("gotSignal"), sTrue);
1365 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1366 QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1367 QCOMPARE(evalJS("slotThisObject == window"), sTrue);
1369 evalJS("gotSignal = false");
1370 m_myObject->emitMySignal();
1371 QCOMPARE(evalJS("gotSignal"), sTrue);
1372 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1374 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined);
1376 evalJS("gotSignal = false");
1377 m_myObject->emitMySignalWithIntArg(123);
1378 QCOMPARE(evalJS("gotSignal"), sTrue);
1379 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1380 QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue);
1382 QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined);
1385 evalJS("myObject.mySignal.disconnect(myHandler)", type);
1386 QCOMPARE(type, sError);
1389 evalJS("gotSignal = false");
1390 QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined);
1391 m_myObject->emitMySignal2(true);
1392 QCOMPARE(evalJS("gotSignal"), sTrue);
1393 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1394 QCOMPARE(evalJS("signalArgs[0]"), sTrue);
1396 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined);
1398 QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction);
1399 QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction);
1400 QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction);
1402 QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined);
1404 evalJS("gotSignal = false");
1405 m_myObject->emitMySignal2();
1406 QCOMPARE(evalJS("gotSignal"), sTrue);
1408 QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined);
1410 // connect(object, function)
1411 evalJS("otherObject = { name:'foo' }");
1412 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1413 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1414 evalJS("gotSignal = false");
1415 m_myObject->emitMySignal();
1416 QCOMPARE(evalJS("gotSignal"), sFalse);
1420 evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type);
1421 QCOMPARE(type, sError);
1424 QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1425 evalJS("gotSignal = false");
1426 m_myObject->emitMySignal();
1427 QCOMPARE(evalJS("gotSignal"), sTrue);
1428 QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1429 QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject"));
1430 QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1431 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo"));
1432 QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1434 evalJS("yetAnotherObject = { name:'bar', func : function() { } }");
1435 QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined);
1436 evalJS("gotSignal = false");
1437 m_myObject->emitMySignal2(true);
1438 QCOMPARE(evalJS("gotSignal"), sTrue);
1439 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1440 QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue);
1441 QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1442 QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar"));
1443 QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined);
1445 QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined);
1446 evalJS("gotSignal = false");
1447 m_myObject->emitMySignal2(true);
1448 QCOMPARE(evalJS("gotSignal"), sTrue);
1449 QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1450 QCOMPARE(evalJS("slotThisObject == myObject"), sTrue);
1451 QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1452 QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined);
1454 // connect(obj, string)
1455 QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined);
1456 QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined);
1457 QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined);
1458 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined);
1460 // check that emitting signals from script works
1463 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1464 m_myObject->resetQtFunctionInvoked();
1465 QCOMPARE(evalJS("myObject.mySignal()"), sUndefined);
1466 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1467 QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined);
1470 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined);
1471 m_myObject->resetQtFunctionInvoked();
1472 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1473 QCOMPARE(m_myObject->qtFunctionInvoked(), 21);
1474 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1475 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1476 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined);
1478 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined);
1479 m_myObject->resetQtFunctionInvoked();
1480 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1481 QCOMPARE(m_myObject->qtFunctionInvoked(), 22);
1482 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1483 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0);
1484 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined);
1486 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined);
1487 m_myObject->resetQtFunctionInvoked();
1488 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1489 QCOMPARE(m_myObject->qtFunctionInvoked(), 23);
1490 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1491 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1492 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined);
1494 // connecting to overloaded slot
1495 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined);
1496 m_myObject->resetQtFunctionInvoked();
1497 QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1498 QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload
1499 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1500 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1501 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined);
1503 QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1504 m_myObject->resetQtFunctionInvoked();
1505 QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined);
1506 QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
1507 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1508 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1509 QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1513 // ### QtScript adds .connect to all functions, WebKit does only to signals/slots
1515 QString ret = evalJS("(function() { }).connect()", type);
1516 QCOMPARE(type, sError);
1517 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1521 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect()", type);
1522 QCOMPARE(type, sError);
1523 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1528 QString ret = evalJS("(function() { }).connect(123)", type);
1529 QCOMPARE(type, sError);
1530 QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1534 QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect(123)", type);
1535 QCOMPARE(type, sError);
1536 QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1541 QString ret = evalJS("myObject.myInvokable.connect(123)", type);
1542 QCOMPARE(type, sError);
1543 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1547 QString ret = evalJS("myObject.myInvokable.connect(function() { })", type);
1548 QCOMPARE(type, sError);
1549 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1554 QString ret = evalJS("myObject.mySignal.connect(123)", type);
1555 QCOMPARE(type, sError);
1556 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function"));
1561 QString ret = evalJS("myObject.mySignal.disconnect()", type);
1562 QCOMPARE(type, sError);
1563 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1567 QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect; o.disconnect()", type);
1568 QCOMPARE(type, sError);
1569 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1572 /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots
1575 QString ret = evalJS("(function() { }).disconnect(123)", type);
1576 QCOMPARE(type, sError);
1577 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal"));
1583 QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type);
1584 QCOMPARE(type, sError);
1585 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1590 QString ret = evalJS("myObject.myInvokable.disconnect(123)", type);
1591 QCOMPARE(type, sError);
1592 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1596 QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type);
1597 QCOMPARE(type, sError);
1598 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1603 QString ret = evalJS("myObject.mySignal.disconnect(123)", type);
1604 QCOMPARE(type, sError);
1605 QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function"));
1610 QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type);
1611 QCOMPARE(type, sError);
1612 QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()"));
1615 // when the wrapper dies, the connection stays alive
1616 QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1617 m_myObject->resetQtFunctionInvoked();
1618 m_myObject->emitMySignal();
1619 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1620 evalJS("myObject = null");
1622 m_myObject->resetQtFunctionInvoked();
1623 m_myObject->emitMySignal();
1624 QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1627 void tst_QWebFrame::classEnums()
1629 // We don't do the meta thing currently, unfortunately!!!
1631 QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue());
1632 m_engine->globalObject().setProperty("MyQObject", myClass);
1634 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()),
1635 MyQObject::FooPolicy);
1636 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()),
1637 MyQObject::BarPolicy);
1638 QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()),
1639 MyQObject::BazPolicy);
1641 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()),
1642 MyQObject::FooStrategy);
1643 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()),
1644 MyQObject::BarStrategy);
1645 QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()),
1646 MyQObject::BazStrategy);
1648 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()),
1649 MyQObject::NoAbility);
1650 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()),
1651 MyQObject::FooAbility);
1652 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()),
1653 MyQObject::BarAbility);
1654 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()),
1655 MyQObject::BazAbility);
1656 QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()),
1657 MyQObject::AllAbility);
1659 // enums from Qt are inherited through prototype
1660 QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()),
1662 QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()),
1665 QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject"));
1667 qRegisterMetaType<MyQObject::Policy>("Policy");
1669 m_myObject->resetQtFunctionInvoked();
1670 QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined);
1671 QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
1672 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1673 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1675 m_myObject->resetQtFunctionInvoked();
1676 QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined);
1677 QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1678 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1679 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1681 m_myObject->resetQtFunctionInvoked();
1683 QVariant ret = evalJS("myObject.myInvokableReturningEnum()");
1684 QCOMPARE(m_myObject->qtFunctionInvoked(), 37);
1685 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1686 QCOMPARE(ret.isVariant());
1688 m_myObject->resetQtFunctionInvoked();
1690 QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()");
1691 QCOMPARE(m_myObject->qtFunctionInvoked(), 38);
1692 QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1693 QCOMPARE(ret.isNumber());
1698 void tst_QWebFrame::classConstructor()
1701 QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine);
1702 m_engine->globalObject().setProperty("MyQObject", myClass);
1704 QString myObj = evalJS("myObj = MyQObject()");
1705 QObject* qobj = myObj.toQObject();
1707 QCOMPARE(qobj->metaObject()->className(), "MyQObject");
1708 QCOMPARE(qobj->parent(), (QObject*)0);
1710 QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine);
1711 m_engine->globalObject().setProperty("QObject", qobjectClass);
1713 QString otherObj = evalJS("otherObj = QObject(myObj)");
1714 QObject* qqobj = otherObj.toQObject();
1715 QVERIFY(qqobj != 0);
1716 QCOMPARE(qqobj->metaObject()->className(), "QObject");
1717 QCOMPARE(qqobj->parent(), qobj);
1723 void tst_QWebFrame::overrideInvokable()
1725 m_myObject->resetQtFunctionInvoked();
1726 QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined);
1727 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1729 /* XFAIL - can't write to functions with RuntimeObject
1730 m_myObject->resetQtFunctionInvoked();
1731 evalJS("myObject.myInvokable = function() { window.a = 123; }");
1732 evalJS("myObject.myInvokable()");
1733 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1734 QCOMPARE(evalJS("window.a").toDouble(), 123.0);
1736 evalJS("myObject.myInvokable = function() { window.a = 456; }");
1737 evalJS("myObject.myInvokable()");
1738 QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1739 QCOMPARE(evalJS("window.a").toDouble(), 456.0);
1742 evalJS("delete myObject.myInvokable");
1743 evalJS("myObject.myInvokable()");
1744 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1747 m_myObject->resetQtFunctionInvoked();
1748 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1749 evalJS("myObject.myInvokable(123)");
1750 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1753 evalJS("delete myObject.myInvokable");
1754 m_myObject->resetQtFunctionInvoked();
1755 // this form (with the '()') is read-only
1756 evalJS("myObject['myInvokable()'] = function() { window.a = 123; }");
1757 evalJS("myObject.myInvokable()");
1758 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1761 void tst_QWebFrame::transferInvokable()
1763 /* XFAIL - can't put to functions with RuntimeObject
1764 m_myObject->resetQtFunctionInvoked();
1765 evalJS("myObject.foozball = myObject.myInvokable");
1766 evalJS("myObject.foozball()");
1767 QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1768 m_myObject->resetQtFunctionInvoked();
1769 evalJS("myObject.foozball = myObject.myInvokableWithIntArg");
1770 evalJS("myObject.foozball(123)");
1771 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1772 m_myObject->resetQtFunctionInvoked();
1773 evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1774 evalJS("myObject.myInvokable(123)");
1775 QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1777 MyOtherQObject other;
1778 m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other);
1779 evalJS("myOtherObject.foo = myObject.foozball");
1780 other.resetQtFunctionInvoked();
1781 evalJS("myOtherObject.foo(456)");
1782 QCOMPARE(other.qtFunctionInvoked(), 1);
1786 void tst_QWebFrame::findChild()
1789 QObject* child = new QObject(m_myObject);
1790 child->setObjectName(QLatin1String("myChildObject"));
1793 QString result = evalJS("myObject.findChild('noSuchChild')");
1794 QCOMPARE(result.isNull());
1798 QString result = evalJS("myObject.findChild('myChildObject')");
1799 QCOMPARE(result.isQObject());
1800 QCOMPARE(result.toQObject(), child);
1807 void tst_QWebFrame::findChildren()
1810 QObject* child = new QObject(m_myObject);
1811 child->setObjectName(QLatin1String("myChildObject"));
1814 QString result = evalJS("myObject.findChildren('noSuchChild')");
1815 QCOMPARE(result.isArray());
1816 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0);
1820 QString result = evalJS("myObject.findChildren('myChildObject')");
1821 QCOMPARE(result.isArray());
1822 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1823 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1826 QObject* namelessChild = new QObject(m_myObject);
1829 QString result = evalJS("myObject.findChildren('myChildObject')");
1830 QCOMPARE(result.isArray());
1831 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1832 QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1835 QObject* anotherChild = new QObject(m_myObject);
1836 anotherChild->setObjectName(QLatin1String("anotherChildObject"));
1839 QString result = evalJS("myObject.findChildren('anotherChildObject')");
1840 QCOMPARE(result.isArray());
1841 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1842 QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild);
1845 anotherChild->setObjectName(QLatin1String("myChildObject"));
1847 QString result = evalJS("myObject.findChildren('myChildObject')");
1848 QCOMPARE(result.isArray());
1849 QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0);
1850 QObject* o1 = result.property(QLatin1String("0")).toQObject();
1851 QObject* o2 = result.property(QLatin1String("1")).toQObject();
1853 QCOMPARE(o1, anotherChild);
1854 QCOMPARE(o2, child);
1856 QCOMPARE(o1, child);
1857 QCOMPARE(o2, anotherChild);
1863 QString result = evalJS("myObject.findChildren()");
1864 QVERIFY(result.isArray());
1866 QCOMPARE(result.property("length"), QLatin1String(count);
1867 for (int i = 0; i < 3; ++i) {
1868 QObject* o = result.property(i).toQObject();
1869 if (o == namelessChild || o == child || o == anotherChild)
1872 QVERIFY(count == 0);
1875 delete anotherChild;
1876 delete namelessChild;
1881 void tst_QWebFrame::overloadedSlots()
1883 // should pick myOverloadedSlot(double)
1884 m_myObject->resetQtFunctionInvoked();
1885 evalJS("myObject.myOverloadedSlot(10)");
1886 QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1888 // should pick myOverloadedSlot(double)
1889 m_myObject->resetQtFunctionInvoked();
1890 evalJS("myObject.myOverloadedSlot(10.0)");
1891 QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1893 // should pick myOverloadedSlot(QString)
1894 m_myObject->resetQtFunctionInvoked();
1895 evalJS("myObject.myOverloadedSlot('10')");
1896 QCOMPARE(m_myObject->qtFunctionInvoked(), 29);
1898 // should pick myOverloadedSlot(bool)
1899 m_myObject->resetQtFunctionInvoked();
1900 evalJS("myObject.myOverloadedSlot(true)");
1901 QCOMPARE(m_myObject->qtFunctionInvoked(), 25);
1903 // should pick myOverloadedSlot(QDateTime)
1904 m_myObject->resetQtFunctionInvoked();
1905 evalJS("myObject.myOverloadedSlot(new Date())");
1906 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1908 // should pick myOverloadedSlot(QRegExp)
1909 m_myObject->resetQtFunctionInvoked();
1910 evalJS("myObject.myOverloadedSlot(new RegExp())");
1911 QCOMPARE(m_myObject->qtFunctionInvoked(), 34);
1913 // should pick myOverloadedSlot(QVariant)
1915 m_myObject->resetQtFunctionInvoked();
1916 QString f = evalJS("myObject.myOverloadedSlot");
1917 f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao")));
1918 QCOMPARE(m_myObject->qtFunctionInvoked(), 35);
1920 // should pick myOverloadedSlot(QObject*)
1921 m_myObject->resetQtFunctionInvoked();
1922 evalJS("myObject.myOverloadedSlot(myObject)");
1923 QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1925 // should pick myOverloadedSlot(QObject*)
1926 m_myObject->resetQtFunctionInvoked();
1927 evalJS("myObject.myOverloadedSlot(null)");
1928 QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1930 // should pick myOverloadedSlot(QStringList)
1931 m_myObject->resetQtFunctionInvoked();
1932 evalJS("myObject.myOverloadedSlot(['hello'])");
1933 QCOMPARE(m_myObject->qtFunctionInvoked(), 42);
1936 void tst_QWebFrame::enumerate_data()
1938 QTest::addColumn<QStringList>("expectedNames");
1940 QTest::newRow("enumerate all")
1942 // meta-object-defined properties:
1946 << "p1" << "p2" << "p4" << "p6"
1947 // dynamic properties
1948 << "dp1" << "dp2" << "dp3"
1950 << "destroyed(QObject*)" << "destroyed()"
1952 // not included because it's private:
1953 // << "_q_reregisterTimers(void*)"
1957 << "mySlot()" << "myOtherSlot()");
1960 void tst_QWebFrame::enumerate()
1962 QFETCH(QStringList, expectedNames);
1964 MyEnumTestQObject enumQObject;
1965 // give it some dynamic properties
1966 enumQObject.setProperty("dp1", "dp1");
1967 enumQObject.setProperty("dp2", "dp2");
1968 enumQObject.setProperty("dp3", "dp3");
1969 m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject);
1971 // enumerate in script
1973 evalJS("var enumeratedProperties = []");
1974 evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }");
1975 QStringList result = evalJSV("enumeratedProperties").toStringList();
1976 QCOMPARE(result.size(), expectedNames.size());
1977 for (int i = 0; i < expectedNames.size(); ++i)
1978 QCOMPARE(result.at(i), expectedNames.at(i));
1982 void tst_QWebFrame::objectDeleted()
1984 MyQObject* qobj = new MyQObject();
1985 m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj);
1986 evalJS("bar.objectName = 'foo';");
1987 QCOMPARE(qobj->objectName(), QLatin1String("foo"));
1988 evalJS("bar.intProperty = 123;");
1989 QCOMPARE(qobj->intProperty(), 123);
1990 qobj->resetQtFunctionInvoked();
1991 evalJS("bar.myInvokable.call(bar);");
1992 QCOMPARE(qobj->qtFunctionInvoked(), 0);
1994 // do this, to ensure that we cache that it implements call
1997 // now delete the object
2000 QCOMPARE(evalJS("typeof bar"), sObject);
2002 // any attempt to access properties of the object should result in an exception
2005 QString ret = evalJS("bar.objectName", type);
2006 QCOMPARE(type, sError);
2007 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2011 QString ret = evalJS("bar.objectName = 'foo'", type);
2012 QCOMPARE(type, sError);
2013 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2016 // myInvokable is stored in member table (since we've accessed it before deletion)
2019 evalJS("bar.myInvokable", type);
2020 QCOMPARE(type, sFunction);
2025 QString ret = evalJS("bar.myInvokable.call(bar);", type);
2026 ret = evalJS("bar.myInvokable(bar)", type);
2027 QCOMPARE(type, sError);
2028 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
2030 // myInvokableWithIntArg is not stored in member table (since we've not accessed it)
2033 QString ret = evalJS("bar.myInvokableWithIntArg", type);
2034 QCOMPARE(type, sError);
2035 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2038 // access from script
2039 evalJS("window.o = bar;");
2042 QString ret = evalJS("o.objectName", type);
2043 QCOMPARE(type, sError);
2044 QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2048 QString ret = evalJS("o.myInvokable()", type);
2049 QCOMPARE(type, sError);
2050 QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
2054 QString ret = evalJS("o.myInvokableWithIntArg(10)", type);
2055 QCOMPARE(type, sError);
2056 QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2060 void tst_QWebFrame::typeConversion()
2062 m_myObject->resetQtFunctionInvoked();
2064 QDateTime localdt(QDate(2008,1,18), QTime(12,31,0));
2065 QDateTime utclocaldt = localdt.toUTC();
2066 QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC);
2068 // Dates in JS (default to local)
2069 evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))");
2070 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2071 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2072 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt);
2074 m_myObject->resetQtFunctionInvoked();
2075 evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))");
2076 QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2077 QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2078 QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt);
2080 // Pushing QDateTimes into JS
2082 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}");
2083 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2084 m_myObject->emitMySignalWithDateTimeArg(localdt);
2085 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2086 evalJS("delete window.__date_equals");
2087 m_myObject->emitMySignalWithDateTimeArg(utclocaldt);
2088 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2089 evalJS("delete window.__date_equals");
2090 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2093 evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }");
2094 evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2095 m_myObject->emitMySignalWithDateTimeArg(utcdt);
2096 QCOMPARE(evalJS("window.__date_equals"), sTrue);
2097 evalJS("delete window.__date_equals");
2098 evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2103 void tst_QWebFrame::symmetricUrl()
2105 QVERIFY(m_view->url().isEmpty());
2107 QCOMPARE(m_view->history()->count(), 0);
2109 QUrl dataUrl("data:text/html,<h1>Test");
2111 m_view->setUrl(dataUrl);
2112 QCOMPARE(m_view->url(), dataUrl);
2113 QCOMPARE(m_view->history()->count(), 0);
2115 // loading is _not_ immediate, so the text isn't set just yet.
2116 QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty());
2118 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2120 QCOMPARE(m_view->history()->count(), 1);
2121 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test"));
2123 QUrl dataUrl2("data:text/html,<h1>Test2");
2124 QUrl dataUrl3("data:text/html,<h1>Test3");
2126 m_view->setUrl(dataUrl2);
2127 m_view->setUrl(dataUrl3);
2129 QCOMPARE(m_view->url(), dataUrl3);
2131 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2133 QCOMPARE(m_view->history()->count(), 2);
2135 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3"));
2138 void tst_QWebFrame::progressSignal()
2140 QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int)));
2142 QUrl dataUrl("data:text/html,<h1>Test");
2143 m_view->setUrl(dataUrl);
2145 ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2147 QVERIFY(progressSpy.size() >= 2);
2149 // WebKit defines initialProgressValue as 10%, not 0%
2150 QCOMPARE(progressSpy.first().first().toInt(), 10);
2152 // But we always end at 100%
2153 QCOMPARE(progressSpy.last().first().toInt(), 100);
2156 void tst_QWebFrame::urlChange()
2158 QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2160 QUrl dataUrl("data:text/html,<h1>Test");
2161 m_view->setUrl(dataUrl);
2163 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2165 QCOMPARE(urlSpy.size(), 1);
2167 QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>");
2168 m_view->setUrl(dataUrl2);
2170 ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2172 QCOMPARE(urlSpy.size(), 2);
2176 void tst_QWebFrame::domCycles()
2178 m_view->setHtml("<html><body>");
2179 QVariant v = m_page->mainFrame()->evaluateJavaScript("document");
2180 QVERIFY(v.type() == QVariant::Map);
2183 class FakeReply : public QNetworkReply {
2187 FakeReply(const QNetworkRequest& request, QObject* parent = 0)
2188 : QNetworkReply(parent)
2190 setOperation(QNetworkAccessManager::GetOperation);
2191 setRequest(request);
2192 if (request.url() == QUrl("qrc:/test1.html")) {
2193 setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html"));
2194 setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html"));
2196 #ifndef QT_NO_OPENSSL
2197 else if (request.url() == QUrl("qrc:/fake-ssl-error.html"))
2198 setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error
2200 else if (request.url() == QUrl("http://abcdef.abcdef/"))
2201 setError(QNetworkReply::HostNotFoundError, tr("Invalid URL"));
2203 open(QIODevice::ReadOnly);
2204 QTimer::singleShot(0, this, SLOT(timeout()));
2210 virtual void abort() {}
2211 virtual void close() {}
2214 qint64 readData(char*, qint64)
2222 if (request().url() == QUrl("qrc://test1.html"))
2223 emit error(this->error());
2224 else if (request().url() == QUrl("http://abcdef.abcdef/"))
2225 emit metaDataChanged();
2226 #ifndef QT_NO_OPENSSL
2227 else if (request().url() == QUrl("qrc:/fake-ssl-error.html"))
2236 class FakeNetworkManager : public QNetworkAccessManager {
2240 FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { }
2243 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
2245 QString url = request.url().toString();
2246 if (op == QNetworkAccessManager::GetOperation) {
2247 if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/")
2248 return new FakeReply(request, this);
2249 #ifndef QT_NO_OPENSSL
2250 else if (url == "qrc:/fake-ssl-error.html") {
2251 FakeReply* reply = new FakeReply(request, this);
2252 QList<QSslError> errors;
2253 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError));
2259 return QNetworkAccessManager::createRequest(op, request, outgoingData);
2263 void tst_QWebFrame::requestedUrl()
2266 QWebFrame* frame = page.mainFrame();
2268 // in few seconds, the image should be completely loaded
2269 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2270 FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
2271 page.setNetworkAccessManager(networkManager);
2273 frame->setUrl(QUrl("qrc:/test1.html"));
2275 QCOMPARE(spy.count(), 1);
2276 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html"));
2277 QCOMPARE(frame->url(), QUrl("qrc:/test2.html"));
2279 frame->setUrl(QUrl("qrc:/non-existent.html"));
2281 QCOMPARE(spy.count(), 2);
2282 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html"));
2283 QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html"));
2285 frame->setUrl(QUrl("http://abcdef.abcdef"));
2287 QCOMPARE(spy.count(), 3);
2288 QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/"));
2289 QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/"));
2291 #ifndef QT_NO_OPENSSL
2292 qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
2293 qRegisterMetaType<QNetworkReply* >("QNetworkReply*");
2295 QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
2296 frame->setUrl(QUrl("qrc:/fake-ssl-error.html"));
2298 QCOMPARE(spy2.count(), 1);
2299 QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html"));
2300 QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html"));
2304 void tst_QWebFrame::javaScriptWindowObjectCleared_data()
2306 QTest::addColumn<QString>("html");
2307 QTest::addColumn<int>("signalCount");
2308 QTest::newRow("with <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 1;
2309 QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0;
2312 void tst_QWebFrame::javaScriptWindowObjectCleared()
2315 QWebFrame* frame = page.mainFrame();
2316 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2317 QFETCH(QString, html);
2318 frame->setHtml(html);
2320 QFETCH(int, signalCount);
2321 QCOMPARE(spy.count(), signalCount);
2324 void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate()
2327 QWebFrame* frame = page.mainFrame();
2328 QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2329 frame->setHtml("<html></html>");
2330 QCOMPARE(spy.count(), 0);
2331 frame->evaluateJavaScript("var a = 'a';");
2332 QCOMPARE(spy.count(), 1);
2333 // no new clear for a new script:
2334 frame->evaluateJavaScript("var a = 1;");
2335 QCOMPARE(spy.count(), 1);
2338 void tst_QWebFrame::setHtml()
2340 QString html("<html><head></head><body><p>hello world</p></body></html>");
2341 m_view->page()->mainFrame()->setHtml(html);
2342 QCOMPARE(m_view->page()->mainFrame()->toHtml(), html);
2345 void tst_QWebFrame::setHtmlWithResource()
2347 QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>");
2350 QWebFrame* frame = page.mainFrame();
2352 // in few seconds, the image should be completey loaded
2353 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2354 frame->setHtml(html);
2356 QCOMPARE(spy.count(), 1);
2358 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2359 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2360 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2365 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />"
2368 "<p id='idP'>some text</p>"
2372 // in few seconds, the CSS should be completey loaded
2373 frame->setHtml(html2);
2375 QCOMPARE(spy.size(), 2);
2377 QWebElement p = frame->documentElement().findAll("p").at(0);
2378 QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red"));
2381 void tst_QWebFrame::setHtmlWithBaseURL()
2383 QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>");
2386 QWebFrame* frame = page.mainFrame();
2388 // in few seconds, the image should be completey loaded
2389 QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2391 frame->setHtml(html, QUrl::fromLocalFile(QDir::currentPath()));
2393 QCOMPARE(spy.count(), 1);
2395 QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2396 QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2397 QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2399 // no history item has to be added.
2400 QCOMPARE(m_view->page()->history()->count(), 0);
2403 class TestNetworkManager : public QNetworkAccessManager
2406 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
2408 QList<QUrl> requestedUrls;
2411 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
2412 requestedUrls.append(request.url());
2413 QNetworkRequest redirectedRequest = request;
2414 redirectedRequest.setUrl(QUrl("data:text/html,<p>hello"));
2415 return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData);
2419 void tst_QWebFrame::ipv6HostEncoding()
2421 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2422 m_page->setNetworkAccessManager(networkManager);
2423 networkManager->requestedUrls.clear();
2425 QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html");
2426 m_view->setHtml("<p>Hi", baseUrl);
2427 m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();"
2428 "r.open('GET', 'http://[::1]/test.xml', false);"
2431 QCOMPARE(networkManager->requestedUrls.count(), 1);
2432 QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml"));
2435 void tst_QWebFrame::metaData()
2437 m_view->setHtml("<html>"
2439 " <meta name=\"description\" content=\"Test description\">"
2440 " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">"
2444 QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData();
2446 QCOMPARE(metaData.count(), 2);
2448 QCOMPARE(metaData.value("description"), QString("Test description"));
2449 QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css"));
2450 QCOMPARE(metaData.value("nonexistant"), QString());
2452 m_view->setHtml("<html>"
2454 " <meta name=\"samekey\" content=\"FirstValue\">"
2455 " <meta name=\"samekey\" content=\"SecondValue\">"
2459 metaData = m_view->page()->mainFrame()->metaData();
2461 QCOMPARE(metaData.count(), 2);
2463 QStringList values = metaData.values("samekey");
2464 QCOMPARE(values.count(), 2);
2466 QVERIFY(values.contains("FirstValue"));
2467 QVERIFY(values.contains("SecondValue"));
2469 QCOMPARE(metaData.value("nonexistant"), QString());
2472 void tst_QWebFrame::popupFocus()
2475 view.setHtml("<html>"
2477 " <select name=\"select\">"
2478 " <option>1</option>"
2479 " <option>2</option>"
2481 " <input type=\"text\"> </input>"
2482 " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">"
2483 "This test checks whether showing and hiding a popup"
2484 "takes the focus away from the webpage."
2488 view.resize(400, 100);
2491 QTRY_VERIFY(view.hasFocus());
2493 // open the popup by clicking. check if focus is on the popup
2494 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(25, 25));
2495 QObject* webpopup = firstChildByClassName(&view, "WebCore::QWebPopup");
2496 QComboBox* combo = qobject_cast<QComboBox*>(webpopup);
2497 QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup
2499 // hide the popup and check if focus is on the page
2501 QTRY_VERIFY(view.hasFocus() && !combo->view()->hasFocus()); // Focus should be back on the WebView
2503 // triple the flashing time, should at least blink twice already
2504 int delay = qApp->cursorFlashTime() * 3;
2506 // focus the lineedit and check if it blinks
2507 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(200, 25));
2508 m_popupTestView = &view;
2509 view.installEventFilter( this );
2510 QTest::qWait(delay);
2511 QVERIFY2(m_popupTestPaintCount >= 4,
2512 "The input field should have a blinking caret");
2515 void tst_QWebFrame::hitTestContent()
2517 QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\">link text</a></body></html>");
2520 QWebFrame* frame = page.mainFrame();
2521 frame->setHtml(html);
2522 page.setViewportSize(QSize(200, 0)); //no height so link is not visible
2523 QWebHitTestResult result = frame->hitTestContent(QPoint(10, 100));
2524 QCOMPARE(result.linkText(), QString("link text"));
2525 QWebElement link = result.linkElement();
2526 QCOMPARE(link.attribute("target"), QString("_foo"));
2529 void tst_QWebFrame::jsByteArray()
2531 QByteArray ba("hello world");
2532 m_myObject->setByteArrayProperty(ba);
2534 // read-only property
2535 QCOMPARE(m_myObject->byteArrayProperty(), ba);
2537 QVariant v = evalJSV("myObject.byteArrayProperty");
2538 QCOMPARE(int(v.type()), int(QVariant::ByteArray));
2540 QCOMPARE(v.toByteArray(), ba);
2543 void tst_QWebFrame::ownership()
2547 QPointer<QObject> ptr = new QObject();
2551 QWebFrame* frame = page.mainFrame();
2552 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership);
2557 QPointer<QObject> ptr = new QObject();
2559 QObject* before = ptr;
2562 QWebFrame* frame = page.mainFrame();
2563 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership);
2565 QVERIFY(ptr == before);
2569 QObject* parent = new QObject();
2570 QObject* child = new QObject(parent);
2572 QWebFrame* frame = page.mainFrame();
2573 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership);
2574 QVariant v = frame->evaluateJavaScript("test");
2575 QCOMPARE(qvariant_cast<QObject*>(v), child);
2577 v = frame->evaluateJavaScript("test");
2578 QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0);
2581 QPointer<QObject> ptr = new QObject();
2585 QWebFrame* frame = page.mainFrame();
2586 frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership);
2588 // no parent, so it should be like ScriptOwnership
2592 QObject* parent = new QObject();
2593 QPointer<QObject> child = new QObject(parent);
2594 QVERIFY(child != 0);
2597 QWebFrame* frame = page.mainFrame();
2598 frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership);
2600 // has parent, so it should be like QtOwnership
2601 QVERIFY(child != 0);
2606 void tst_QWebFrame::nullValue()
2608 QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null");
2609 QVERIFY(v.isNull());
2612 void tst_QWebFrame::baseUrl_data()
2614 QTest::addColumn<QString>("html");
2615 QTest::addColumn<QUrl>("loadUrl");
2616 QTest::addColumn<QUrl>("url");
2617 QTest::addColumn<QUrl>("baseUrl");
2619 QTest::newRow("null") << QString() << QUrl()
2620 << QUrl("about:blank") << QUrl("about:blank");
2622 QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/")
2623 << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/");
2625 QString html = "<html>"
2627 "<base href=\"http://foobaz.bar/\" />"
2630 QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/")
2631 << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/");
2634 void tst_QWebFrame::baseUrl()
2636 QFETCH(QString, html);
2637 QFETCH(QUrl, loadUrl);
2639 QFETCH(QUrl, baseUrl);
2641 m_page->mainFrame()->setHtml(html, loadUrl);
2642 QCOMPARE(m_page->mainFrame()->url(), url);
2643 QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl);
2646 void tst_QWebFrame::hasSetFocus()
2648 QString html("<html><body><p>top</p>" \
2649 "<iframe width='80%' height='30%'/>" \
2652 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
2653 m_page->mainFrame()->setHtml(html);
2656 QCOMPARE(loadSpy.size(), 1);
2658 QList<QWebFrame*> children = m_page->mainFrame()->childFrames();
2659 QWebFrame* frame = children.at(0);
2660 QString innerHtml("<html><body><p>another iframe</p>" \
2661 "<iframe width='80%' height='30%'/>" \
2663 frame->setHtml(innerHtml);
2666 QCOMPARE(loadSpy.size(), 2);
2668 m_page->mainFrame()->setFocus();
2669 QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2671 for (int i = 0; i < children.size(); ++i) {
2672 children.at(i)->setFocus();
2673 QTRY_VERIFY(children.at(i)->hasFocus());
2674 QVERIFY(!m_page->mainFrame()->hasFocus());
2677 m_page->mainFrame()->setFocus();
2678 QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2681 void tst_QWebFrame::render()
2683 QString html("<html>" \
2685 "body, iframe { margin: 0px; border: none; }" \
2687 "<body><iframe width='100px' height='100px'/></body>" \
2691 page.mainFrame()->setHtml(html);
2693 QList<QWebFrame*> frames = page.mainFrame()->childFrames();
2694 QWebFrame *frame = frames.at(0);
2695 QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>");
2696 frame->setHtml(innerHtml);
2700 QSize size = page.mainFrame()->contentsSize();
2701 page.setViewportSize(size);
2703 // render contents layer only (the iframe is smaller than the image, so it will have scrollbars)
2704 QPainter painter1(&picture);
2705 frame->render(&painter1, QWebFrame::ContentsLayer);
2708 QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width());
2709 QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height());
2711 // render everything, should be the size of the iframe
2712 QPainter painter2(&picture);
2713 frame->render(&painter2, QWebFrame::AllLayers);
2716 QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px
2717 QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px
2720 void tst_QWebFrame::scrollPosition()
2722 // enlarged image in a small viewport, to provoke the scrollbars to appear
2723 QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>");
2726 page.setViewportSize(QSize(200, 200));
2728 QWebFrame* frame = page.mainFrame();
2729 frame->setHtml(html);
2730 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
2731 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
2733 // try to set the scroll offset programmatically
2734 frame->setScrollPosition(QPoint(23, 29));
2735 QCOMPARE(frame->scrollPosition().x(), 23);
2736 QCOMPARE(frame->scrollPosition().y(), 29);
2738 int x = frame->evaluateJavaScript("window.scrollX").toInt();
2739 int y = frame->evaluateJavaScript("window.scrollY").toInt();
2744 void tst_QWebFrame::evaluateWillCauseRepaint()
2747 QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">"
2748 "junk</div>bottom</body></html>");
2754 view.page()->mainFrame()->evaluateJavaScript(
2755 "document.getElementById('junk').style.display = 'none';");
2757 ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect)));
2762 class TestFactory : public QObject
2767 : obj(0), counter(0)
2770 Q_INVOKABLE QObject* getNewObject()
2773 obj = new QObject(this);
2774 obj->setObjectName(QLatin1String("test") + QString::number(++counter));
2783 void tst_QWebFrame::qObjectWrapperWithSameIdentity()
2785 m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>"
2786 "<body><span id='span1'>test</span></body>");
2788 QWebFrame* mainFrame = m_view->page()->mainFrame();
2789 QCOMPARE(mainFrame->toPlainText(), QString("test"));
2791 mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership);
2793 mainFrame->evaluateJavaScript("triggerBug();");
2794 QCOMPARE(mainFrame->toPlainText(), QString("test1"));
2796 mainFrame->evaluateJavaScript("triggerBug();");
2797 QCOMPARE(mainFrame->toPlainText(), QString("test2"));
2800 QTEST_MAIN(tst_QWebFrame)
2801 #include "tst_qwebframe.moc"