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