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