2008-09-23 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebKit / qt / tests / qwebframe / tst_qwebframe.cpp
1 /*
2     Copyright (C) 2008 Trolltech ASA
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19
20
21 #include <QtTest/QtTest>
22
23 #include <qwebpage.h>
24 #include <qwidget.h>
25 #include <qwebview.h>
26 #include <qwebframe.h>
27 #include <qwebhistory.h>
28 #include <QRegExp>
29 #include <QNetworkRequest>
30 //TESTED_CLASS=
31 //TESTED_FILES=
32
33 // Task 160192
34 /**
35  * Starts an event loop that runs until the given signal is received.
36  Optionally the event loop
37  * can return earlier on a timeout.
38  *
39  * \return \p true if the requested signal was received
40  *         \p false on timeout
41  */
42 static bool waitForSignal(QObject* obj, const char* signal, int timeout = 0)
43 {
44     QEventLoop loop;
45     QObject::connect(obj, signal, &loop, SLOT(quit()));
46     QTimer timer;
47     QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
48     if (timeout > 0) {
49         QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
50         timer.setSingleShot(true);
51         timer.start(timeout);
52     }
53     loop.exec();
54     return timeoutSpy.isEmpty();
55 }
56
57 /* Mostly a test for the JavaScript related parts of QWebFrame */
58
59
60 struct CustomType {
61     QString string;
62 };
63 Q_DECLARE_METATYPE(CustomType)
64
65 Q_DECLARE_METATYPE(QBrush*)
66 Q_DECLARE_METATYPE(QObjectList)
67 Q_DECLARE_METATYPE(QList<int>)
68 Q_DECLARE_METATYPE(Qt::BrushStyle)
69 Q_DECLARE_METATYPE(QVariantList)
70 Q_DECLARE_METATYPE(QVariantMap)
71
72 class MyQObject : public QObject
73 {
74     Q_OBJECT
75
76     Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty)
77     Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty)
78     Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty)
79     Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
80     Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty)
81     Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty)
82     Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty)
83     Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false)
84     Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty)
85     Q_PROPERTY(int readOnlyProperty READ readOnlyProperty)
86     Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
87     Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType)
88     Q_ENUMS(Policy Strategy)
89     Q_FLAGS(Ability)
90
91 public:
92     enum Policy {
93         FooPolicy = 0,
94         BarPolicy,
95         BazPolicy
96     };
97
98     enum Strategy {
99         FooStrategy = 10,
100         BarStrategy,
101         BazStrategy
102     };
103
104     enum AbilityFlag {
105         NoAbility  = 0x000,
106         FooAbility = 0x001,
107         BarAbility = 0x080,
108         BazAbility = 0x200,
109         AllAbility = FooAbility | BarAbility | BazAbility
110     };
111
112     Q_DECLARE_FLAGS(Ability, AbilityFlag)
113
114     MyQObject(QObject* parent = 0)
115         : QObject(parent),
116             m_intValue(123),
117             m_variantValue(QLatin1String("foo")),
118             m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))),
119             m_stringValue(QLatin1String("bar")),
120             m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")),
121             m_brushValue(QColor(10, 20, 30, 40)),
122             m_hiddenValue(456.0),
123             m_writeOnlyValue(789),
124             m_readOnlyValue(987),
125             m_qtFunctionInvoked(-1) { }
126
127     ~MyQObject() { }
128
129     int intProperty() const {
130         return m_intValue;
131     }
132     void setIntProperty(int value) {
133         m_intValue = value;
134     }
135
136     QVariant variantProperty() const {
137         return m_variantValue;
138     }
139     void setVariantProperty(const QVariant &value) {
140         m_variantValue = value;
141     }
142
143     QVariantList variantListProperty() const {
144         return m_variantListValue;
145     }
146     void setVariantListProperty(const QVariantList &value) {
147         m_variantListValue = value;
148     }
149
150     QString stringProperty() const {
151         return m_stringValue;
152     }
153     void setStringProperty(const QString &value) {
154         m_stringValue = value;
155     }
156
157     QStringList stringListProperty() const {
158         return m_stringListValue;
159     }
160     void setStringListProperty(const QStringList &value) {
161         m_stringListValue = value;
162     }
163
164     QByteArray byteArrayProperty() const {
165         return m_byteArrayValue;
166     }
167     void setByteArrayProperty(const QByteArray &value) {
168         m_byteArrayValue = value;
169     }
170
171     QBrush brushProperty() const {
172         return m_brushValue;
173     }
174     Q_INVOKABLE void setBrushProperty(const QBrush &value) {
175         m_brushValue = value;
176     }
177
178     double hiddenProperty() const {
179         return m_hiddenValue;
180     }
181     void setHiddenProperty(double value) {
182         m_hiddenValue = value;
183     }
184
185     int writeOnlyProperty() const {
186         return m_writeOnlyValue;
187     }
188     void setWriteOnlyProperty(int value) {
189         m_writeOnlyValue = value;
190     }
191
192     int readOnlyProperty() const {
193         return m_readOnlyValue;
194     }
195
196     QKeySequence shortcut() const {
197         return m_shortcut;
198     }
199     void setShortcut(const QKeySequence &seq) {
200         m_shortcut = seq;
201     }
202
203     CustomType propWithCustomType() const {
204         return m_customType;
205     }
206     void setPropWithCustomType(const CustomType &c) {
207         m_customType = c;
208     }
209
210     int qtFunctionInvoked() const {
211         return m_qtFunctionInvoked;
212     }
213
214     QVariantList qtFunctionActuals() const {
215         return m_actuals;
216     }
217
218     void resetQtFunctionInvoked() {
219         m_qtFunctionInvoked = -1;
220         m_actuals.clear();
221     }
222
223     Q_INVOKABLE void myInvokable() {
224         m_qtFunctionInvoked = 0;
225     }
226     Q_INVOKABLE void myInvokableWithIntArg(int arg) {
227         m_qtFunctionInvoked = 1;
228         m_actuals << arg;
229     }
230     Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) {
231         m_qtFunctionInvoked = 2;
232         m_actuals << arg;
233     }
234     Q_INVOKABLE void myInvokableWithFloatArg(float arg) {
235         m_qtFunctionInvoked = 3;
236         m_actuals << arg;
237     }
238     Q_INVOKABLE void myInvokableWithDoubleArg(double arg) {
239         m_qtFunctionInvoked = 4;
240         m_actuals << arg;
241     }
242     Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) {
243         m_qtFunctionInvoked = 5;
244         m_actuals << arg;
245     }
246     Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) {
247         m_qtFunctionInvoked = 6;
248         m_actuals << arg1 << arg2;
249     }
250     Q_INVOKABLE int myInvokableReturningInt() {
251         m_qtFunctionInvoked = 7;
252         return 123;
253     }
254     Q_INVOKABLE qlonglong myInvokableReturningLongLong() {
255         m_qtFunctionInvoked = 39;
256         return 456;
257     }
258     Q_INVOKABLE QString myInvokableReturningString() {
259         m_qtFunctionInvoked = 8;
260         return QLatin1String("ciao");
261     }
262     Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload
263         m_qtFunctionInvoked = 9;
264         m_actuals << arg1 << arg2;
265     }
266     Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) {
267         m_qtFunctionInvoked = 10;
268         m_actuals << policy;
269     }
270     Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) {
271         m_qtFunctionInvoked = 36;
272         m_actuals << policy;
273     }
274     Q_INVOKABLE Policy myInvokableReturningEnum() {
275         m_qtFunctionInvoked = 37;
276         return BazPolicy;
277     }
278     Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() {
279         m_qtFunctionInvoked = 38;
280         return BazPolicy;
281     }
282     Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() {
283         m_qtFunctionInvoked = 11;
284         return QVector<int>();
285     }
286     Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) {
287         m_qtFunctionInvoked = 12;
288     }
289     Q_INVOKABLE QObject* myInvokableReturningQObjectStar() {
290         m_qtFunctionInvoked = 13;
291         return this;
292     }
293     Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) {
294         m_qtFunctionInvoked = 14;
295         m_actuals << qVariantFromValue(lst);
296         return lst;
297     }
298     Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) {
299         m_qtFunctionInvoked = 15;
300         m_actuals << v;
301         return v;
302     }
303     Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) {
304         m_qtFunctionInvoked = 16;
305         m_actuals << vm;
306         return vm;
307     }
308     Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) {
309         m_qtFunctionInvoked = 17;
310         m_actuals << qVariantFromValue(lst);
311         return lst;
312     }
313     Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) {
314         m_qtFunctionInvoked = 18;
315         m_actuals << qVariantFromValue(obj);
316         return obj;
317     }
318     Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) {
319         m_qtFunctionInvoked = 19;
320         m_actuals << qVariantFromValue(brush);
321         return brush;
322     }
323     Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) {
324         m_qtFunctionInvoked = 43;
325         m_actuals << qVariantFromValue(style);
326     }
327     Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) {
328         m_qtFunctionInvoked = 44;
329         m_actuals << qVariantFromValue(arg);
330     }
331     Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) {
332         m_qtFunctionInvoked = 45;
333         m_actuals << qVariantFromValue(arg);
334     }
335     Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) {
336         m_qtFunctionInvoked = 46;
337         m_actuals << qVariantFromValue(arg);
338     }
339     Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") {
340         m_qtFunctionInvoked = 47;
341         m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2);
342     }
343     Q_INVOKABLE QObject& myInvokableReturningRef() {
344         m_qtFunctionInvoked = 48;
345         return *this;
346     }
347     Q_INVOKABLE const QObject& myInvokableReturningConstRef() const {
348         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49;
349         return *this;
350     }
351     Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) {
352         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50;
353         m_actuals << qVariantFromValue(arg);
354     }
355     Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) {
356         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51;
357         m_actuals << qVariantFromValue(arg);
358     }
359
360     void emitMySignal() {
361         emit mySignal();
362     }
363     void emitMySignalWithIntArg(int arg) {
364         emit mySignalWithIntArg(arg);
365     }
366     void emitMySignal2(bool arg) {
367         emit mySignal2(arg);
368     }
369     void emitMySignal2() {
370         emit mySignal2();
371     }
372     void emitMySignalWithDateTimeArg(QDateTime dt) {
373         emit mySignalWithDateTimeArg(dt);
374     }
375     void emitMySignalWithRegexArg(QRegExp r) {
376         emit mySignalWithRegexArg(r);
377     }
378
379 public Q_SLOTS:
380     void mySlot() {
381         m_qtFunctionInvoked = 20;
382     }
383     void mySlotWithIntArg(int arg) {
384         m_qtFunctionInvoked = 21;
385         m_actuals << arg;
386     }
387     void mySlotWithDoubleArg(double arg) {
388         m_qtFunctionInvoked = 22;
389         m_actuals << arg;
390     }
391     void mySlotWithStringArg(const QString &arg) {
392         m_qtFunctionInvoked = 23;
393         m_actuals << arg;
394     }
395
396     void myOverloadedSlot() {
397         m_qtFunctionInvoked = 24;
398     }
399     void myOverloadedSlot(QObject* arg) {
400         m_qtFunctionInvoked = 41;
401         m_actuals << arg;
402     }
403     void myOverloadedSlot(bool arg) {
404         m_qtFunctionInvoked = 25;
405         m_actuals << arg;
406     }
407     void myOverloadedSlot(const QStringList &arg) {
408         m_qtFunctionInvoked = 42;
409         m_actuals << arg;
410     }
411     void myOverloadedSlot(double arg) {
412         m_qtFunctionInvoked = 26;
413         m_actuals << arg;
414     }
415     void myOverloadedSlot(float arg) {
416         m_qtFunctionInvoked = 27;
417         m_actuals << arg;
418     }
419     void myOverloadedSlot(int arg) {
420         m_qtFunctionInvoked = 28;
421         m_actuals << arg;
422     }
423     void myOverloadedSlot(const QString &arg) {
424         m_qtFunctionInvoked = 29;
425         m_actuals << arg;
426     }
427     void myOverloadedSlot(const QColor &arg) {
428         m_qtFunctionInvoked = 30;
429         m_actuals << arg;
430     }
431     void myOverloadedSlot(const QBrush &arg) {
432         m_qtFunctionInvoked = 31;
433         m_actuals << arg;
434     }
435     void myOverloadedSlot(const QDateTime &arg) {
436         m_qtFunctionInvoked = 32;
437         m_actuals << arg;
438     }
439     void myOverloadedSlot(const QDate &arg) {
440         m_qtFunctionInvoked = 33;
441         m_actuals << arg;
442     }
443     void myOverloadedSlot(const QRegExp &arg) {
444         m_qtFunctionInvoked = 34;
445         m_actuals << arg;
446     }
447     void myOverloadedSlot(const QVariant &arg) {
448         m_qtFunctionInvoked = 35;
449         m_actuals << arg;
450     }
451
452     void qscript_call(int arg) {
453         m_qtFunctionInvoked = 40;
454         m_actuals << arg;
455     }
456
457 protected Q_SLOTS:
458     void myProtectedSlot() {
459         m_qtFunctionInvoked = 36;
460     }
461
462 private Q_SLOTS:
463     void myPrivateSlot() { }
464
465 Q_SIGNALS:
466     void mySignal();
467     void mySignalWithIntArg(int arg);
468     void mySignalWithDoubleArg(double arg);
469     void mySignal2(bool arg = false);
470     void mySignalWithDateTimeArg(QDateTime dt);
471     void mySignalWithRegexArg(QRegExp r);
472
473 private:
474     int m_intValue;
475     QVariant m_variantValue;
476     QVariantList m_variantListValue;
477     QString m_stringValue;
478     QStringList m_stringListValue;
479     QByteArray m_byteArrayValue;
480     QBrush m_brushValue;
481     double m_hiddenValue;
482     int m_writeOnlyValue;
483     int m_readOnlyValue;
484     QKeySequence m_shortcut;
485     CustomType m_customType;
486     int m_qtFunctionInvoked;
487     QVariantList m_actuals;
488 };
489
490 class MyOtherQObject : public MyQObject
491 {
492 public:
493     MyOtherQObject(QObject* parent = 0)
494         : MyQObject(parent) { }
495 };
496
497 class MyEnumTestQObject : public QObject
498 {
499     Q_OBJECT
500     Q_PROPERTY(QString p1 READ p1)
501     Q_PROPERTY(QString p2 READ p2)
502     Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false)
503     Q_PROPERTY(QString p4 READ p4)
504     Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false)
505     Q_PROPERTY(QString p6 READ p6)
506 public:
507     MyEnumTestQObject(QObject* parent = 0)
508         : QObject(parent) { }
509     QString p1() const {
510         return QLatin1String("p1");
511     }
512     QString p2() const {
513         return QLatin1String("p2");
514     }
515     QString p3() const {
516         return QLatin1String("p3");
517     }
518     QString p4() const {
519         return QLatin1String("p4");
520     }
521     QString p5() const {
522         return QLatin1String("p5");
523     }
524     QString p6() const {
525         return QLatin1String("p5");
526     }
527 public Q_SLOTS:
528     void mySlot() { }
529     void myOtherSlot() { }
530 Q_SIGNALS:
531     void mySignal();
532 };
533
534 class tst_QWebFrame : public QObject
535 {
536     Q_OBJECT
537
538 public:
539     tst_QWebFrame();
540     virtual ~tst_QWebFrame();
541
542 public slots:
543     void init();
544     void cleanup();
545
546 private slots:
547     void getSetStaticProperty();
548     void getSetDynamicProperty();
549     void getSetChildren();
550     void callQtInvokable();
551     void connectAndDisconnect();
552     void classEnums();
553     void classConstructor();
554     void overrideInvokable();
555     void transferInvokable();
556     void findChild();
557     void findChildren();
558     void overloadedSlots();
559     void enumerate_data();
560     void enumerate();
561     void objectDeleted();
562     void typeConversion();
563     void symmetricUrl();
564     void progressSignal();
565     void domCycles();
566     void setHtml();
567     void ipv6HostEncoding();
568 private:
569     QString  evalJS(const QString&s) {
570         // Convert an undefined return variant to the string "undefined"
571         QVariant ret = evalJSV(s);
572         if (ret.userType() == QMetaType::Void)
573             return "undefined";
574         else
575             return ret.toString();
576     }
577     QVariant evalJSV(const QString &s) {
578         return m_page->mainFrame()->evaluateJavaScript(s);
579     }
580
581     QString  evalJS(const QString&s, QString& type) {
582         return evalJSV(s, type).toString();
583     }
584     QVariant evalJSV(const QString &s, QString& type) {
585         // As a special measure, if we get an exception we set the type to 'error'
586         // (in ecma, an Error object has typeof object, but qtscript has a convenience function)
587         // Similarly, an array is an object, but we'd prefer to have a type of 'array'
588         // Also, consider a QMetaType::Void QVariant to be undefined
589         QString escaped = s;
590         escaped.replace('\'', "\\'"); // Don't preescape your single quotes!
591         evalJS("var retvalue;\
592                var typevalue; \
593                try {\
594                retvalue = eval('" + escaped + "'); \
595                typevalue = typeof retvalue; \
596                if (retvalue instanceof Array) \
597                typevalue = 'array'; \
598            } \
599                catch(e) {\
600                retvalue = e.name + ': ' + e.message;\
601                typevalue = 'error';\
602            }");
603         QVariant ret = evalJSV("retvalue");
604         if (ret.userType() != QMetaType::Void)
605             type = evalJS("typevalue");
606         else {
607             ret = QString("undefined");
608             type = sUndefined;
609         }
610         evalJS("delete retvalue; delete typevalue");
611         return ret;
612     }
613
614     const QString sTrue;
615     const QString sFalse;
616     const QString sUndefined;
617     const QString sArray;
618     const QString sFunction;
619     const QString sError;
620     const QString sString;
621     const QString sObject;
622     const QString sNumber;
623
624 private:
625     QWebView* m_view;
626     QWebPage* m_page;
627     MyQObject* m_myObject;
628 };
629
630 tst_QWebFrame::tst_QWebFrame()
631     : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"),
632         sString("string"), sObject("object"), sNumber("number")
633 {
634 }
635
636 tst_QWebFrame::~tst_QWebFrame()
637 {
638 }
639
640 void tst_QWebFrame::init()
641 {
642     m_view = new QWebView();
643     m_page = m_view->page();
644     m_myObject = new MyQObject();
645     m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject);
646 }
647
648 void tst_QWebFrame::cleanup()
649 {
650     delete m_view;
651     delete m_myObject;
652 }
653
654 void tst_QWebFrame::getSetStaticProperty()
655 {
656     QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined);
657
658     // initial value (set in MyQObject constructor)
659     {
660         QString type;
661         QVariant ret = evalJSV("myObject.intProperty", type);
662         QCOMPARE(type, sNumber);
663         QCOMPARE(ret.type(), QVariant::Double);
664         QCOMPARE(ret.toInt(), 123);
665     }
666     QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue);
667
668     {
669         QString type;
670         QVariant ret = evalJSV("myObject.variantProperty", type);
671         QCOMPARE(type, sString);
672         QCOMPARE(ret.type(), QVariant::String);
673         QCOMPARE(ret.toString(), QLatin1String("foo"));
674     }
675     QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue);
676
677     {
678         QString type;
679         QVariant ret = evalJSV("myObject.stringProperty", type);
680         QCOMPARE(type, sString);
681         QCOMPARE(ret.type(), QVariant::String);
682         QCOMPARE(ret.toString(), QLatin1String("bar"));
683     }
684     QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue);
685
686     {
687         QString type;
688         QVariant ret = evalJSV("myObject.variantListProperty", type);
689         QCOMPARE(type, sArray);
690         QCOMPARE(ret.type(), QVariant::List);
691         QVariantList vl = ret.value<QVariantList>();
692         QCOMPARE(vl.size(), 2);
693         QCOMPARE(vl.at(0).toInt(), 123);
694         QCOMPARE(vl.at(1).toString(), QLatin1String("foo"));
695     }
696     QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue);
697     QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue);
698     QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue);
699
700     {
701         QString type;
702         QVariant ret = evalJSV("myObject.stringListProperty", type);
703         QCOMPARE(type, sArray);
704         QCOMPARE(ret.type(), QVariant::List);
705         QVariantList vl = ret.value<QVariantList>();
706         QCOMPARE(vl.size(), 2);
707         QCOMPARE(vl.at(0).toString(), QLatin1String("zig"));
708         QCOMPARE(vl.at(1).toString(), QLatin1String("zag"));
709     }
710     QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue);
711     QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
712     QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig"));
713     QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
714     QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag"));
715
716     // property change in C++ should be reflected in script
717     m_myObject->setIntProperty(456);
718     QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue);
719     m_myObject->setIntProperty(789);
720     QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue);
721
722     m_myObject->setVariantProperty(QLatin1String("bar"));
723     QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue);
724     m_myObject->setVariantProperty(42);
725     QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue);
726     m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
727 //XFAIL
728 //  QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant);
729
730     m_myObject->setStringProperty(QLatin1String("baz"));
731     QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue);
732     m_myObject->setStringProperty(QLatin1String("zab"));
733     QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue);
734
735     // property change in script should be reflected in C++
736     QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123"));
737     QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
738     QCOMPARE(m_myObject->intProperty(), 123);
739     QCOMPARE(evalJS("myObject.intProperty = 'ciao!';"
740                     "myObject.intProperty == 0"), sTrue);
741     QCOMPARE(m_myObject->intProperty(), 0);
742     QCOMPARE(evalJS("myObject.intProperty = '123';"
743                     "myObject.intProperty == 123"), sTrue);
744     QCOMPARE(m_myObject->intProperty(), 123);
745
746     QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao"));
747     QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao"));
748     QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao"));
749     QCOMPARE(evalJS("myObject.stringProperty = 123;"
750                     "myObject.stringProperty"), QLatin1String("123"));
751     QCOMPARE(m_myObject->stringProperty(), QLatin1String("123"));
752
753     QCOMPARE(evalJS("myObject.variantProperty = 'foo';"
754                     "myObject.variantProperty.valueOf()"), QLatin1String("foo"));
755     QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo")));
756     QCOMPARE(evalJS("myObject.variantProperty = 42;"
757                     "myObject.variantProperty").toDouble(), 42.0);
758     QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0);
759
760
761     QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];"
762                     "myObject.variantListProperty.length == 3"), sTrue);
763     QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue);
764     QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two"));
765     QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue);
766
767     QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];"
768                     "myObject.stringListProperty.length == 3"), sTrue);
769     QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
770     QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue);
771     QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
772     QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two"));
773     QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString);
774     QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true"));
775
776     // try to delete
777     QCOMPARE(evalJS("delete myObject.intProperty"), sFalse);
778     QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
779
780     QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse);
781     QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0);
782
783     // non-scriptable property
784     QCOMPARE(m_myObject->hiddenProperty(), 456.0);
785     QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined);
786     QEXPECT_FAIL("", "undefined properties not supported", Continue);
787     QCOMPARE(evalJS("myObject.hiddenProperty = 123;"
788                     "myObject.hiddenProperty == 123"), sTrue);
789     QCOMPARE(m_myObject->hiddenProperty(), 456.0);
790
791     // write-only property
792     QCOMPARE(m_myObject->writeOnlyProperty(), 789);
793     QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined);
794     QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;"
795                     "typeof myObject.writeOnlyProperty"), sUndefined);
796     QCOMPARE(m_myObject->writeOnlyProperty(), 123);
797
798     // read-only property
799     QCOMPARE(m_myObject->readOnlyProperty(), 987);
800     QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue);
801     QCOMPARE(evalJS("myObject.readOnlyProperty = 654;"
802                     "myObject.readOnlyProperty == 987"), sTrue);
803     QCOMPARE(m_myObject->readOnlyProperty(), 987);
804 }
805
806 void tst_QWebFrame::getSetDynamicProperty()
807 {
808     // initially the object does not have the property
809     // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty
810
811     //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
812     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
813
814     // add a dynamic property in C++
815     QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
816     //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue);
817     QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue);
818     QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue);
819
820     // property change in script should be reflected in C++
821     QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';"
822                     "myObject.dynamicProperty"), QLatin1String("foo"));
823     QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo"));
824
825     // delete the property (XFAIL - can't delete properties)
826     QEXPECT_FAIL("", "can't delete properties", Continue);
827     QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue);
828     /*
829     QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false);
830     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
831     //    QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
832     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
833     */
834 }
835
836 void tst_QWebFrame::getSetChildren()
837 {
838     // initially the object does not have the child
839     // (again, no hasOwnProperty)
840
841     //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
842     QCOMPARE(evalJS("typeof myObject.child"), sUndefined);
843
844     // add a child
845     MyQObject* child = new MyQObject(m_myObject);
846     child->setObjectName("child");
847 //  QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue);
848     QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue);
849
850     // add a grandchild
851     MyQObject* grandChild = new MyQObject(child);
852     grandChild->setObjectName("grandChild");
853 //  QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue);
854     QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue);
855
856     // delete grandchild
857     delete grandChild;
858 //  QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse);
859     QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue);
860
861     // delete child
862     delete child;
863 //  QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
864     QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue);
865 }
866
867 Q_DECLARE_METATYPE(QVector<int>)
868 Q_DECLARE_METATYPE(QVector<double>)
869 Q_DECLARE_METATYPE(QVector<QString>)
870
871 void tst_QWebFrame::callQtInvokable()
872 {
873     qRegisterMetaType<QObjectList>();
874
875     m_myObject->resetQtFunctionInvoked();
876     QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined);
877     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
878     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
879
880     // extra arguments should silently be ignored
881     m_myObject->resetQtFunctionInvoked();
882     QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined);
883     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
884     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
885
886     m_myObject->resetQtFunctionInvoked();
887     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined);
888     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
889     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
890     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
891
892     m_myObject->resetQtFunctionInvoked();
893     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined);
894     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
895     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
896     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
897
898     m_myObject->resetQtFunctionInvoked();
899     QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined);
900     QCOMPARE(m_myObject->qtFunctionInvoked(), 2);
901     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
902     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123));
903
904     m_myObject->resetQtFunctionInvoked();
905     QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined);
906     QCOMPARE(m_myObject->qtFunctionInvoked(), 3);
907     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
908     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
909
910     m_myObject->resetQtFunctionInvoked();
911     QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined);
912     QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
913     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
914     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
915
916     m_myObject->resetQtFunctionInvoked();
917     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined);
918     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
919     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
920     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao"));
921
922     m_myObject->resetQtFunctionInvoked();
923     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined);
924     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
925     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
926     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
927
928     m_myObject->resetQtFunctionInvoked();
929     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined);
930     QCOMPARE(m_myObject->qtFunctionInvoked(), 6);
931     QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
932     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
933     QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
934
935     m_myObject->resetQtFunctionInvoked();
936     QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123"));
937     QCOMPARE(m_myObject->qtFunctionInvoked(), 7);
938     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
939
940     m_myObject->resetQtFunctionInvoked();
941     QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456"));
942     QCOMPARE(m_myObject->qtFunctionInvoked(), 39);
943     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
944
945     m_myObject->resetQtFunctionInvoked();
946     QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao"));
947     QCOMPARE(m_myObject->qtFunctionInvoked(), 8);
948     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
949
950     m_myObject->resetQtFunctionInvoked();
951     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined);
952     QCOMPARE(m_myObject->qtFunctionInvoked(), 9);
953     QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
954     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
955     QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
956
957     m_myObject->resetQtFunctionInvoked();
958     QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined);
959     QCOMPARE(m_myObject->qtFunctionInvoked(), 44);
960     m_myObject->resetQtFunctionInvoked();
961     {
962         QString type;
963         QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type);
964         QCOMPARE(type, sError);
965         QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n    myInvokableWithVoidStarArg(void*)"));
966         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
967     }
968
969     m_myObject->resetQtFunctionInvoked();
970     {
971         QString type;
972         QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type);
973         QCOMPARE(type, sError);
974         QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n    myInvokableWithAmbiguousArg(int)\n    myInvokableWithAmbiguousArg(uint)"));
975     }
976
977     m_myObject->resetQtFunctionInvoked();
978     {
979         QString type;
980         QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type);
981         QCOMPARE(type, sUndefined);
982         QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
983         QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
984         QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
985         QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello"));
986     }
987
988     m_myObject->resetQtFunctionInvoked();
989     {
990         QString type;
991         QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type);
992         QCOMPARE(type, sUndefined);
993         QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
994         QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
995         QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
996         QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString());
997     }
998
999     // calling function that returns (const)ref
1000     m_myObject->resetQtFunctionInvoked();
1001     {
1002         QString type;
1003         QString ret = evalJS("typeof myObject.myInvokableReturningRef()");
1004         QCOMPARE(ret, sUndefined);
1005         //QVERIFY(!m_engine->hasUncaughtException());
1006         QCOMPARE(m_myObject->qtFunctionInvoked(), 48);
1007     }
1008
1009     m_myObject->resetQtFunctionInvoked();
1010     {
1011         QString type;
1012         QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()");
1013         QCOMPARE(ret, sUndefined);
1014         //QVERIFY(!m_engine->hasUncaughtException());
1015         QCOMPARE(m_myObject->qtFunctionInvoked(), 49);
1016     }
1017
1018     m_myObject->resetQtFunctionInvoked();
1019     {
1020         QString type;
1021         QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type);
1022         QCOMPARE(m_myObject->qtFunctionInvoked(), 13);
1023         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1024         QCOMPARE(type, sObject);
1025         QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1026     }
1027
1028     m_myObject->resetQtFunctionInvoked();
1029     {
1030         QString type;
1031         QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type);
1032         QCOMPARE(m_myObject->qtFunctionInvoked(), 14);
1033         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1034         QCOMPARE(type, sArray);
1035         QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList
1036         QVariantList vl = qvariant_cast<QVariantList>(ret);
1037         QCOMPARE(vl.count(), 1);
1038     }
1039
1040     m_myObject->resetQtFunctionInvoked();
1041     {
1042         QString type;
1043         m_myObject->setVariantProperty(QVariant(123));
1044         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type);
1045         QCOMPARE(type, sNumber);
1046         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1047         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1048         QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty());
1049         QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int
1050         QCOMPARE(ret.toInt(),123);
1051     }
1052
1053     /* XFAIL - variant support
1054     m_myObject->resetQtFunctionInvoked();
1055     {
1056         m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
1057         QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
1058         QVERIFY(ret.isVariant());
1059         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1060         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1061         QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0));
1062         QCOMPARE(ret.toVariant(), m_myObject->variantProperty());
1063     }
1064     */
1065
1066     m_myObject->resetQtFunctionInvoked();
1067     {
1068         QString type;
1069         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type);
1070         QCOMPARE(type, sNumber);
1071         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1072         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1073         QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123));
1074         QCOMPARE(ret.userType(), int(QMetaType::Double));
1075         QCOMPARE(ret.toInt(),123);
1076     }
1077
1078     m_myObject->resetQtFunctionInvoked();
1079     {
1080         QString type;
1081         QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type);
1082         QCOMPARE(m_myObject->qtFunctionInvoked(), 16);
1083         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1084
1085         QVariant v = m_myObject->qtFunctionActuals().at(0);
1086         QCOMPARE(v.userType(), int(QMetaType::QVariantMap));
1087
1088         QVariantMap vmap = qvariant_cast<QVariantMap>(v);
1089         QCOMPARE(vmap.keys().size(), 2);
1090         QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1091         QCOMPARE(vmap.value("a"), QVariant(123));
1092         QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1093         QCOMPARE(vmap.value("b"), QVariant("ciao"));
1094
1095         QCOMPARE(type, sObject);
1096
1097         QCOMPARE(ret.userType(), int(QMetaType::QVariantMap));
1098         vmap = qvariant_cast<QVariantMap>(ret);
1099         QCOMPARE(vmap.keys().size(), 2);
1100         QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1101         QCOMPARE(vmap.value("a"), QVariant(123));
1102         QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1103         QCOMPARE(vmap.value("b"), QVariant("ciao"));
1104     }
1105
1106     m_myObject->resetQtFunctionInvoked();
1107     {
1108         QString type;
1109         QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type);
1110         QCOMPARE(m_myObject->qtFunctionInvoked(), 17);
1111         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1112         QVariant v = m_myObject->qtFunctionActuals().at(0);
1113         QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
1114         QList<int> ilst = qvariant_cast<QList<int> >(v);
1115         QCOMPARE(ilst.size(), 2);
1116         QCOMPARE(ilst.at(0), 1);
1117         QCOMPARE(ilst.at(1), 5);
1118
1119         QCOMPARE(type, sArray);
1120         QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist
1121         QVariantList vlst = qvariant_cast<QVariantList>(ret);
1122         QCOMPARE(vlst.size(), 2);
1123         QCOMPARE(vlst.at(0).toInt(), 1);
1124         QCOMPARE(vlst.at(1).toInt(), 5);
1125     }
1126
1127     m_myObject->resetQtFunctionInvoked();
1128     {
1129         QString type;
1130         QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type);
1131         QCOMPARE(m_myObject->qtFunctionInvoked(), 18);
1132         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1133         QVariant v = m_myObject->qtFunctionActuals().at(0);
1134         QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1135         QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject);
1136
1137         QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1138         QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject);
1139
1140         QCOMPARE(type, sObject);
1141     }
1142
1143     m_myObject->resetQtFunctionInvoked();
1144     {
1145         // no implicit conversion from integer to QObject*
1146         QString type;
1147         evalJS("myObject.myInvokableWithQObjectStarArg(123)", type);
1148         QCOMPARE(type, sError);
1149     }
1150
1151     /*
1152     m_myObject->resetQtFunctionInvoked();
1153     {
1154         QString fun = evalJS("myObject.myInvokableWithQBrushArg");
1155         Q_ASSERT(fun.isFunction());
1156         QColor color(10, 20, 30, 40);
1157         // QColor should be converted to a QBrush
1158         QVariant ret = fun.call(QString(), QStringList()
1159                                     << qScriptValueFromValue(m_engine, color));
1160         QCOMPARE(m_myObject->qtFunctionInvoked(), 19);
1161         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1162         QVariant v = m_myObject->qtFunctionActuals().at(0);
1163         QCOMPARE(v.userType(), int(QMetaType::QBrush));
1164         QCOMPARE(qvariant_cast<QColor>(v), color);
1165
1166         QCOMPARE(qscriptvalue_cast<QColor>(ret), color);
1167     }
1168     */
1169
1170     // private slots should not be part of the QObject binding
1171     QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined);
1172
1173     // protected slots should be fine
1174     m_myObject->resetQtFunctionInvoked();
1175     evalJS("myObject.myProtectedSlot()");
1176     QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1177
1178     // call with too few arguments
1179     {
1180         QString type;
1181         QString ret = evalJS("myObject.myInvokableWithIntArg()", type);
1182         QCOMPARE(type, sError);
1183         QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n    myInvokableWithIntArg(int,int)\n    myInvokableWithIntArg(int)"));
1184     }
1185
1186     // call function where not all types have been registered
1187     m_myObject->resetQtFunctionInvoked();
1188     {
1189         QString type;
1190         QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type);
1191         QCOMPARE(type, sError);
1192         QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'"));
1193         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1194     }
1195
1196     // call function with incompatible argument type
1197     m_myObject->resetQtFunctionInvoked();
1198     {
1199         QString type;
1200         QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type);
1201         QCOMPARE(type, sError);
1202         QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n    myInvokableWithQBrushArg(QBrush)"));
1203         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1204     }
1205 }
1206
1207 void tst_QWebFrame::connectAndDisconnect()
1208 {
1209     // connect(function)
1210     QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction);
1211     QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction);
1212     QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction);
1213
1214     {
1215         QString type;
1216         evalJS("myObject.mySignal.connect(123)", type);
1217         QCOMPARE(type, sError);
1218     }
1219
1220     evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }");
1221
1222     QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined);
1223
1224     evalJS("gotSignal = false");
1225     evalJS("myObject.mySignal()");
1226     QCOMPARE(evalJS("gotSignal"), sTrue);
1227     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1228     QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1229     QCOMPARE(evalJS("slotThisObject == window"), sTrue);
1230
1231     evalJS("gotSignal = false");
1232     m_myObject->emitMySignal();
1233     QCOMPARE(evalJS("gotSignal"), sTrue);
1234     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1235
1236     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined);
1237
1238     evalJS("gotSignal = false");
1239     m_myObject->emitMySignalWithIntArg(123);
1240     QCOMPARE(evalJS("gotSignal"), sTrue);
1241     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1242     QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue);
1243
1244     QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined);
1245     {
1246         QString type;
1247         evalJS("myObject.mySignal.disconnect(myHandler)", type);
1248         QCOMPARE(type, sError);
1249     }
1250
1251     evalJS("gotSignal = false");
1252     QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined);
1253     m_myObject->emitMySignal2(true);
1254     QCOMPARE(evalJS("gotSignal"), sTrue);
1255     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1256     QCOMPARE(evalJS("signalArgs[0]"), sTrue);
1257
1258     QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined);
1259
1260     QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction);
1261     QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction);
1262     QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction);
1263
1264     QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined);
1265
1266     evalJS("gotSignal = false");
1267     m_myObject->emitMySignal2();
1268     QCOMPARE(evalJS("gotSignal"), sTrue);
1269
1270     QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined);
1271
1272     // connect(object, function)
1273     evalJS("otherObject = { name:'foo' }");
1274     QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1275     QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1276     evalJS("gotSignal = false");
1277     m_myObject->emitMySignal();
1278     QCOMPARE(evalJS("gotSignal"), sFalse);
1279
1280     {
1281         QString type;
1282         evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type);
1283         QCOMPARE(type, sError);
1284     }
1285
1286     QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1287     evalJS("gotSignal = false");
1288     m_myObject->emitMySignal();
1289     QCOMPARE(evalJS("gotSignal"), sTrue);
1290     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1291     QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject"));
1292     QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1293     QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo"));
1294     QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1295
1296     evalJS("yetAnotherObject = { name:'bar', func : function() { } }");
1297     QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined);
1298     evalJS("gotSignal = false");
1299     m_myObject->emitMySignal2(true);
1300     QCOMPARE(evalJS("gotSignal"), sTrue);
1301     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1302     QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue);
1303     QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1304     QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar"));
1305     QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined);
1306
1307     QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined);
1308     evalJS("gotSignal = false");
1309     m_myObject->emitMySignal2(true);
1310     QCOMPARE(evalJS("gotSignal"), sTrue);
1311     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1312     QCOMPARE(evalJS("slotThisObject == myObject"), sTrue);
1313     QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1314     QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined);
1315
1316     // connect(obj, string)
1317     QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined);
1318     QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined);
1319     QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined);
1320     QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined);
1321
1322     // check that emitting signals from script works
1323
1324     // no arguments
1325     QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1326     m_myObject->resetQtFunctionInvoked();
1327     QCOMPARE(evalJS("myObject.mySignal()"), sUndefined);
1328     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1329     QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined);
1330
1331     // one argument
1332     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined);
1333     m_myObject->resetQtFunctionInvoked();
1334     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1335     QCOMPARE(m_myObject->qtFunctionInvoked(), 21);
1336     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1337     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1338     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined);
1339
1340     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined);
1341     m_myObject->resetQtFunctionInvoked();
1342     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1343     QCOMPARE(m_myObject->qtFunctionInvoked(), 22);
1344     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1345     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0);
1346     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined);
1347
1348     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined);
1349     m_myObject->resetQtFunctionInvoked();
1350     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1351     QCOMPARE(m_myObject->qtFunctionInvoked(), 23);
1352     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1353     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1354     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined);
1355
1356     // connecting to overloaded slot
1357     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined);
1358     m_myObject->resetQtFunctionInvoked();
1359     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1360     QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload
1361     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1362     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1363     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined);
1364
1365     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1366     m_myObject->resetQtFunctionInvoked();
1367     QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined);
1368     QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
1369     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1370     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1371     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1372
1373     // erroneous input
1374     {
1375         // ### QtScript adds .connect to all functions, WebKit does only to signals/slots
1376         QString type;
1377         QString ret = evalJS("(function() { }).connect()", type);
1378         QCOMPARE(type, sError);
1379         QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1380     }
1381     {
1382         QString type;
1383         QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect;  o.connect()", type);
1384         QCOMPARE(type, sError);
1385         QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1386     }
1387
1388     {
1389         QString type;
1390         QString ret = evalJS("(function() { }).connect(123)", type);
1391         QCOMPARE(type, sError);
1392         QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1393     }
1394     {
1395         QString type;
1396         QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect;  o.connect(123)", type);
1397         QCOMPARE(type, sError);
1398         QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1399     }
1400
1401     {
1402         QString type;
1403         QString ret = evalJS("myObject.myInvokable.connect(123)", type);
1404         QCOMPARE(type, sError);
1405         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1406     }
1407     {
1408         QString type;
1409         QString ret = evalJS("myObject.myInvokable.connect(function() { })", type);
1410         QCOMPARE(type, sError);
1411         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1412     }
1413
1414     {
1415         QString type;
1416         QString ret = evalJS("myObject.mySignal.connect(123)", type);
1417         QCOMPARE(type, sError);
1418         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function"));
1419     }
1420
1421     {
1422         QString type;
1423         QString ret = evalJS("myObject.mySignal.disconnect()", type);
1424         QCOMPARE(type, sError);
1425         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1426     }
1427     {
1428         QString type;
1429         QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect;  o.disconnect()", type);
1430         QCOMPARE(type, sError);
1431         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1432     }
1433
1434     /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots
1435     {
1436         QString type;
1437         QString ret = evalJS("(function() { }).disconnect(123)", type);
1438         QCOMPARE(type, sError);
1439         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal"));
1440     }
1441     */
1442
1443     {
1444         QString type;
1445         QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type);
1446         QCOMPARE(type, sError);
1447         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1448     }
1449
1450     {
1451         QString type;
1452         QString ret = evalJS("myObject.myInvokable.disconnect(123)", type);
1453         QCOMPARE(type, sError);
1454         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1455     }
1456     {
1457         QString type;
1458         QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type);
1459         QCOMPARE(type, sError);
1460         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1461     }
1462
1463     {
1464         QString type;
1465         QString ret = evalJS("myObject.mySignal.disconnect(123)", type);
1466         QCOMPARE(type, sError);
1467         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function"));
1468     }
1469
1470     {
1471         QString type;
1472         QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type);
1473         QCOMPARE(type, sError);
1474         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()"));
1475     }
1476
1477     // when the wrapper dies, the connection stays alive
1478     QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1479     m_myObject->resetQtFunctionInvoked();
1480     m_myObject->emitMySignal();
1481     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1482     evalJS("myObject = null");
1483     evalJS("gc()");
1484     m_myObject->resetQtFunctionInvoked();
1485     m_myObject->emitMySignal();
1486     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1487 }
1488
1489 void tst_QWebFrame::classEnums()
1490 {
1491     // We don't do the meta thing currently, unfortunately!!!
1492     /*
1493     QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue());
1494     m_engine->globalObject().setProperty("MyQObject", myClass);
1495
1496     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()),
1497              MyQObject::FooPolicy);
1498     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()),
1499              MyQObject::BarPolicy);
1500     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()),
1501              MyQObject::BazPolicy);
1502
1503     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()),
1504              MyQObject::FooStrategy);
1505     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()),
1506              MyQObject::BarStrategy);
1507     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()),
1508              MyQObject::BazStrategy);
1509
1510     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()),
1511              MyQObject::NoAbility);
1512     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()),
1513              MyQObject::FooAbility);
1514     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()),
1515              MyQObject::BarAbility);
1516     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()),
1517              MyQObject::BazAbility);
1518     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()),
1519              MyQObject::AllAbility);
1520
1521     // enums from Qt are inherited through prototype
1522     QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()),
1523              Qt::StrongFocus);
1524     QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()),
1525              Qt::Key_Left);
1526
1527     QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject"));
1528
1529     qRegisterMetaType<MyQObject::Policy>("Policy");
1530
1531     m_myObject->resetQtFunctionInvoked();
1532     QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined);
1533     QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
1534     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1535     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1536
1537     m_myObject->resetQtFunctionInvoked();
1538     QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined);
1539     QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1540     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1541     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1542
1543     m_myObject->resetQtFunctionInvoked();
1544     {
1545         QVariant ret = evalJS("myObject.myInvokableReturningEnum()");
1546         QCOMPARE(m_myObject->qtFunctionInvoked(), 37);
1547         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1548         QCOMPARE(ret.isVariant());
1549     }
1550     m_myObject->resetQtFunctionInvoked();
1551     {
1552         QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()");
1553         QCOMPARE(m_myObject->qtFunctionInvoked(), 38);
1554         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1555         QCOMPARE(ret.isNumber());
1556     }
1557     */
1558 }
1559
1560 void tst_QWebFrame::classConstructor()
1561 {
1562     /*
1563     QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine);
1564     m_engine->globalObject().setProperty("MyQObject", myClass);
1565
1566     QString myObj = evalJS("myObj = MyQObject()");
1567     QObject* qobj = myObj.toQObject();
1568     QVERIFY(qobj != 0);
1569     QCOMPARE(qobj->metaObject()->className(), "MyQObject");
1570     QCOMPARE(qobj->parent(), (QObject*)0);
1571
1572     QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine);
1573     m_engine->globalObject().setProperty("QObject", qobjectClass);
1574
1575     QString otherObj = evalJS("otherObj = QObject(myObj)");
1576     QObject* qqobj = otherObj.toQObject();
1577     QVERIFY(qqobj != 0);
1578     QCOMPARE(qqobj->metaObject()->className(), "QObject");
1579     QCOMPARE(qqobj->parent(), qobj);
1580
1581     delete qobj;
1582     */
1583 }
1584
1585 void tst_QWebFrame::overrideInvokable()
1586 {
1587     m_myObject->resetQtFunctionInvoked();
1588     QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined);
1589     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1590
1591     /* XFAIL - can't write to functions with RuntimeObject
1592     m_myObject->resetQtFunctionInvoked();
1593     evalJS("myObject.myInvokable = function() { window.a = 123; }");
1594     evalJS("myObject.myInvokable()");
1595     QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1596     QCOMPARE(evalJS("window.a").toDouble(), 123.0);
1597
1598     evalJS("myObject.myInvokable = function() { window.a = 456; }");
1599     evalJS("myObject.myInvokable()");
1600     QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1601     QCOMPARE(evalJS("window.a").toDouble(), 456.0);
1602     */
1603
1604     evalJS("delete myObject.myInvokable");
1605     evalJS("myObject.myInvokable()");
1606     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1607
1608     /* XFAIL - ditto
1609     m_myObject->resetQtFunctionInvoked();
1610     evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1611     evalJS("myObject.myInvokable(123)");
1612     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1613     */
1614
1615     evalJS("delete myObject.myInvokable");
1616     m_myObject->resetQtFunctionInvoked();
1617     // this form (with the '()') is read-only
1618     evalJS("myObject['myInvokable()'] = function() { window.a = 123; }");
1619     evalJS("myObject.myInvokable()");
1620     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1621 }
1622
1623 void tst_QWebFrame::transferInvokable()
1624 {
1625     /* XFAIL - can't put to functions with RuntimeObject
1626     m_myObject->resetQtFunctionInvoked();
1627     evalJS("myObject.foozball = myObject.myInvokable");
1628     evalJS("myObject.foozball()");
1629     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1630     m_myObject->resetQtFunctionInvoked();
1631     evalJS("myObject.foozball = myObject.myInvokableWithIntArg");
1632     evalJS("myObject.foozball(123)");
1633     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1634     m_myObject->resetQtFunctionInvoked();
1635     evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1636     evalJS("myObject.myInvokable(123)");
1637     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1638
1639     MyOtherQObject other;
1640     m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other);
1641     evalJS("myOtherObject.foo = myObject.foozball");
1642     other.resetQtFunctionInvoked();
1643     evalJS("myOtherObject.foo(456)");
1644     QCOMPARE(other.qtFunctionInvoked(), 1);
1645     */
1646 }
1647
1648 void tst_QWebFrame::findChild()
1649 {
1650     /*
1651     QObject* child = new QObject(m_myObject);
1652     child->setObjectName(QLatin1String("myChildObject"));
1653
1654     {
1655         QString result = evalJS("myObject.findChild('noSuchChild')");
1656         QCOMPARE(result.isNull());
1657     }
1658
1659     {
1660         QString result = evalJS("myObject.findChild('myChildObject')");
1661         QCOMPARE(result.isQObject());
1662         QCOMPARE(result.toQObject(), child);
1663     }
1664
1665     delete child;
1666     */
1667 }
1668
1669 void tst_QWebFrame::findChildren()
1670 {
1671     /*
1672     QObject* child = new QObject(m_myObject);
1673     child->setObjectName(QLatin1String("myChildObject"));
1674
1675     {
1676         QString result = evalJS("myObject.findChildren('noSuchChild')");
1677         QCOMPARE(result.isArray());
1678         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0);
1679     }
1680
1681     {
1682         QString result = evalJS("myObject.findChildren('myChildObject')");
1683         QCOMPARE(result.isArray());
1684         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1685         QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1686     }
1687
1688     QObject* namelessChild = new QObject(m_myObject);
1689
1690     {
1691         QString result = evalJS("myObject.findChildren('myChildObject')");
1692         QCOMPARE(result.isArray());
1693         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1694         QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1695     }
1696
1697     QObject* anotherChild = new QObject(m_myObject);
1698     anotherChild->setObjectName(QLatin1String("anotherChildObject"));
1699
1700     {
1701         QString result = evalJS("myObject.findChildren('anotherChildObject')");
1702         QCOMPARE(result.isArray());
1703         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1704         QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild);
1705     }
1706
1707     anotherChild->setObjectName(QLatin1String("myChildObject"));
1708     {
1709         QString result = evalJS("myObject.findChildren('myChildObject')");
1710         QCOMPARE(result.isArray());
1711         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0);
1712         QObject* o1 = result.property(QLatin1String("0")).toQObject();
1713         QObject* o2 = result.property(QLatin1String("1")).toQObject();
1714         if (o1 != child) {
1715             QCOMPARE(o1, anotherChild);
1716             QCOMPARE(o2, child);
1717         } else {
1718             QCOMPARE(o1, child);
1719             QCOMPARE(o2, anotherChild);
1720         }
1721     }
1722
1723     // find all
1724     {
1725         QString result = evalJS("myObject.findChildren()");
1726         QVERIFY(result.isArray());
1727         int count = 3;
1728         QCOMPARE(result.property("length"), QLatin1String(count);
1729         for (int i = 0; i < 3; ++i) {
1730             QObject* o = result.property(i).toQObject();
1731             if (o == namelessChild || o == child || o == anotherChild)
1732                 --count;
1733         }
1734         QVERIFY(count == 0);
1735     }
1736
1737     delete anotherChild;
1738     delete namelessChild;
1739     delete child;
1740     */
1741 }
1742
1743 void tst_QWebFrame::overloadedSlots()
1744 {
1745     // should pick myOverloadedSlot(double)
1746     m_myObject->resetQtFunctionInvoked();
1747     evalJS("myObject.myOverloadedSlot(10)");
1748     QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1749
1750     // should pick myOverloadedSlot(double)
1751     m_myObject->resetQtFunctionInvoked();
1752     evalJS("myObject.myOverloadedSlot(10.0)");
1753     QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1754
1755     // should pick myOverloadedSlot(QString)
1756     m_myObject->resetQtFunctionInvoked();
1757     evalJS("myObject.myOverloadedSlot('10')");
1758     QCOMPARE(m_myObject->qtFunctionInvoked(), 29);
1759
1760     // should pick myOverloadedSlot(bool)
1761     m_myObject->resetQtFunctionInvoked();
1762     evalJS("myObject.myOverloadedSlot(true)");
1763     QCOMPARE(m_myObject->qtFunctionInvoked(), 25);
1764
1765     // should pick myOverloadedSlot(QDateTime)
1766     m_myObject->resetQtFunctionInvoked();
1767     evalJS("myObject.myOverloadedSlot(new Date())");
1768     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1769
1770     // should pick myOverloadedSlot(QRegExp)
1771     m_myObject->resetQtFunctionInvoked();
1772     evalJS("myObject.myOverloadedSlot(new RegExp())");
1773     QCOMPARE(m_myObject->qtFunctionInvoked(), 34);
1774
1775     // should pick myOverloadedSlot(QVariant)
1776     /* XFAIL
1777     m_myObject->resetQtFunctionInvoked();
1778     QString f = evalJS("myObject.myOverloadedSlot");
1779     f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao")));
1780     QCOMPARE(m_myObject->qtFunctionInvoked(), 35);
1781     */
1782     // should pick myOverloadedSlot(QObject*)
1783     m_myObject->resetQtFunctionInvoked();
1784     evalJS("myObject.myOverloadedSlot(myObject)");
1785     QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1786
1787     // should pick myOverloadedSlot(QObject*)
1788     m_myObject->resetQtFunctionInvoked();
1789     evalJS("myObject.myOverloadedSlot(null)");
1790     QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1791
1792     // should pick myOverloadedSlot(QStringList)
1793     m_myObject->resetQtFunctionInvoked();
1794     evalJS("myObject.myOverloadedSlot(['hello'])");
1795     QCOMPARE(m_myObject->qtFunctionInvoked(), 42);
1796 }
1797
1798 void tst_QWebFrame::enumerate_data()
1799 {
1800     QTest::addColumn<QStringList>("expectedNames");
1801
1802     QTest::newRow("enumerate all")
1803     << (QStringList()
1804         // meta-object-defined properties:
1805         //   inherited
1806         << "objectName"
1807         //   non-inherited
1808         << "p1" << "p2" << "p4" << "p6"
1809         // dynamic properties
1810         << "dp1" << "dp2" << "dp3"
1811         // inherited slots
1812         << "destroyed(QObject*)" << "destroyed()"
1813         << "deleteLater()"
1814         // not included because it's private:
1815         // << "_q_reregisterTimers(void*)"
1816         // signals
1817         << "mySignal()"
1818         // slots
1819         << "mySlot()" << "myOtherSlot()");
1820 }
1821
1822 void tst_QWebFrame::enumerate()
1823 {
1824     QFETCH(QStringList, expectedNames);
1825
1826     MyEnumTestQObject enumQObject;
1827     // give it some dynamic properties
1828     enumQObject.setProperty("dp1", "dp1");
1829     enumQObject.setProperty("dp2", "dp2");
1830     enumQObject.setProperty("dp3", "dp3");
1831     m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject);
1832
1833     // enumerate in script
1834     {
1835         evalJS("var enumeratedProperties = []");
1836         evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }");
1837         QStringList result = evalJSV("enumeratedProperties").toStringList();
1838         QCOMPARE(result.size(), expectedNames.size());
1839         for (int i = 0; i < expectedNames.size(); ++i)
1840             QCOMPARE(result.at(i), expectedNames.at(i));
1841     }
1842 }
1843
1844 void tst_QWebFrame::objectDeleted()
1845 {
1846     MyQObject* qobj = new MyQObject();
1847     m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj);
1848     evalJS("bar.objectName = 'foo';");
1849     QCOMPARE(qobj->objectName(), QLatin1String("foo"));
1850     evalJS("bar.intProperty = 123;");
1851     QCOMPARE(qobj->intProperty(), 123);
1852     qobj->resetQtFunctionInvoked();
1853     evalJS("bar.myInvokable.call(bar);");
1854     QCOMPARE(qobj->qtFunctionInvoked(), 0);
1855
1856     // do this, to ensure that we cache that it implements call
1857     evalJS("bar()");
1858
1859     // now delete the object
1860     delete qobj;
1861
1862     QCOMPARE(evalJS("typeof bar"), sObject);
1863
1864     // any attempt to access properties of the object should result in an exception
1865     {
1866         QString type;
1867         QString ret = evalJS("bar.objectName", type);
1868         QCOMPARE(type, sError);
1869         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
1870     }
1871     {
1872         QString type;
1873         QString ret = evalJS("bar.objectName = 'foo'", type);
1874         QCOMPARE(type, sError);
1875         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
1876     }
1877
1878     // myInvokable is stored in member table (since we've accessed it before deletion)
1879     {
1880         QString type;
1881         evalJS("bar.myInvokable", type);
1882         QCOMPARE(type, sFunction);
1883     }
1884
1885     {
1886         QString type;
1887         QString ret = evalJS("bar.myInvokable.call(bar);", type);
1888         ret = evalJS("bar.myInvokable(bar)", type);
1889         QCOMPARE(type, sError);
1890         QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
1891     }
1892     // myInvokableWithIntArg is not stored in member table (since we've not accessed it)
1893     {
1894         QString type;
1895         QString ret = evalJS("bar.myInvokableWithIntArg", type);
1896         QCOMPARE(type, sError);
1897         QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
1898     }
1899
1900     // access from script
1901     evalJS("window.o = bar;");
1902     {
1903         QString type;
1904         QString ret = evalJS("o.objectName", type);
1905         QCOMPARE(type, sError);
1906         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
1907     }
1908     {
1909         QString type;
1910         QString ret = evalJS("o.myInvokable()", type);
1911         QCOMPARE(type, sError);
1912         QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
1913     }
1914     {
1915         QString type;
1916         QString ret = evalJS("o.myInvokableWithIntArg(10)", type);
1917         QCOMPARE(type, sError);
1918         QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
1919     }
1920 }
1921
1922 void tst_QWebFrame::typeConversion()
1923 {
1924     m_myObject->resetQtFunctionInvoked();
1925
1926     QDateTime localdt(QDate(2008,1,18), QTime(12,31,0));
1927     QDateTime utclocaldt = localdt.toUTC();
1928     QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC);
1929
1930     // Dates in JS (default to local)
1931     evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))");
1932     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1933     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1934     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt);
1935
1936     m_myObject->resetQtFunctionInvoked();
1937     evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))");
1938     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1939     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1940     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt);
1941
1942     // Pushing QDateTimes into JS
1943     // Local
1944     evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}");
1945     evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
1946     m_myObject->emitMySignalWithDateTimeArg(localdt);
1947     QCOMPARE(evalJS("window.__date_equals"), sTrue);
1948     evalJS("delete window.__date_equals");
1949     m_myObject->emitMySignalWithDateTimeArg(utclocaldt);
1950     QCOMPARE(evalJS("window.__date_equals"), sTrue);
1951     evalJS("delete window.__date_equals");
1952     evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
1953
1954     // UTC
1955     evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }");
1956     evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
1957     m_myObject->emitMySignalWithDateTimeArg(utcdt);
1958     QCOMPARE(evalJS("window.__date_equals"), sTrue);
1959     evalJS("delete window.__date_equals");
1960     evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
1961
1962     // ### RegExps
1963 }
1964
1965 void tst_QWebFrame::symmetricUrl()
1966 {
1967     QVERIFY(m_view->url().isEmpty());
1968
1969     QCOMPARE(m_view->history()->count(), 0);
1970
1971     QUrl dataUrl("data:text/html,<h1>Test");
1972
1973     m_view->setUrl(dataUrl);
1974     QCOMPARE(m_view->url(), dataUrl);
1975     QCOMPARE(m_view->history()->count(), 0);
1976
1977     // loading is _not_ immediate, so the text isn't set just yet.
1978     QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty());
1979
1980     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
1981
1982     QCOMPARE(m_view->history()->count(), 1);
1983     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test"));
1984
1985     QUrl dataUrl2("data:text/html,<h1>Test2");
1986     QUrl dataUrl3("data:text/html,<h1>Test3");
1987
1988     m_view->setUrl(dataUrl2);
1989     m_view->setUrl(dataUrl3);
1990
1991     QCOMPARE(m_view->url(), dataUrl3);
1992
1993     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
1994
1995     QCOMPARE(m_view->history()->count(), 2);
1996
1997     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3"));
1998 }
1999
2000 void tst_QWebFrame::progressSignal()
2001 {
2002     QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int)));
2003
2004     QUrl dataUrl("data:text/html,<h1>Test");
2005     m_view->setUrl(dataUrl);
2006
2007     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2008
2009     QVERIFY(progressSpy.size() >= 2);
2010
2011     // WebKit defines initialProgressValue as 10%, not 0%
2012     QCOMPARE(progressSpy.first().first().toInt(), 10);
2013
2014     // But we always end at 100%
2015     QCOMPARE(progressSpy.last().first().toInt(), 100);
2016 }
2017
2018 void tst_QWebFrame::domCycles()
2019 {
2020     m_view->setHtml("<html><body>");
2021     QVariant v = m_page->mainFrame()->evaluateJavaScript("document");
2022     QVERIFY(v.type() == QVariant::Map);
2023 }
2024
2025 void tst_QWebFrame::setHtml()
2026 {
2027     QString html("<html><body><p>hello world</p></body></html>");
2028     m_view->page()->mainFrame()->setHtml(html);
2029     QCOMPARE(m_view->page()->mainFrame()->toHtml(), html);
2030 }
2031
2032 class TestNetworkManager : public QNetworkAccessManager
2033 {
2034 public:
2035     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
2036
2037     QList<QUrl> requestedUrls;
2038
2039 protected:
2040     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
2041         requestedUrls.append(request.url());
2042         QNetworkRequest redirectedRequest = request;
2043         redirectedRequest.setUrl(QUrl("data:text/html,<p>hello"));
2044         return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData);
2045     }
2046 };
2047
2048 void tst_QWebFrame::ipv6HostEncoding()
2049 {
2050     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2051     m_page->setNetworkAccessManager(networkManager);
2052     networkManager->requestedUrls.clear();
2053
2054     QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html");
2055     m_view->setHtml("<p>Hi", baseUrl);
2056     m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();"
2057             "r.open('GET', 'http://[::1]/test.xml', false);"
2058             "r.send(null);"
2059             );
2060     QCOMPARE(networkManager->requestedUrls.count(), 1);
2061     QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml"));
2062 }
2063
2064 QTEST_MAIN(tst_QWebFrame)
2065 #include "tst_qwebframe.moc"