OSDN Git Service

am 381c572c: (-s ours) am f0073a99: Use STLPort instead of our stripped version....
[android-x86/external-webkit.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 <qwebelement.h>
25 #include <qwidget.h>
26 #include <qwebview.h>
27 #include <qwebframe.h>
28 #include <qwebhistory.h>
29 #include <QAbstractItemView>
30 #include <QApplication>
31 #include <QComboBox>
32 #include <QPicture>
33 #include <QRegExp>
34 #include <QNetworkRequest>
35 #include <QNetworkReply>
36 #ifndef QT_NO_OPENSSL
37 #include <qsslerror.h>
38 #endif
39 #include "../util.h"
40
41 struct CustomType {
42     QString string;
43 };
44 Q_DECLARE_METATYPE(CustomType)
45
46 Q_DECLARE_METATYPE(QBrush*)
47 Q_DECLARE_METATYPE(QObjectList)
48 Q_DECLARE_METATYPE(QList<int>)
49 Q_DECLARE_METATYPE(Qt::BrushStyle)
50 Q_DECLARE_METATYPE(QVariantList)
51 Q_DECLARE_METATYPE(QVariantMap)
52
53 class MyQObject : public QObject
54 {
55     Q_OBJECT
56
57     Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty)
58     Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty)
59     Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty)
60     Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
61     Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty)
62     Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty)
63     Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty)
64     Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false)
65     Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty)
66     Q_PROPERTY(int readOnlyProperty READ readOnlyProperty)
67     Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
68     Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType)
69     Q_ENUMS(Policy Strategy)
70     Q_FLAGS(Ability)
71
72 public:
73     enum Policy {
74         FooPolicy = 0,
75         BarPolicy,
76         BazPolicy
77     };
78
79     enum Strategy {
80         FooStrategy = 10,
81         BarStrategy,
82         BazStrategy
83     };
84
85     enum AbilityFlag {
86         NoAbility  = 0x000,
87         FooAbility = 0x001,
88         BarAbility = 0x080,
89         BazAbility = 0x200,
90         AllAbility = FooAbility | BarAbility | BazAbility
91     };
92
93     Q_DECLARE_FLAGS(Ability, AbilityFlag)
94
95     MyQObject(QObject* parent = 0)
96         : QObject(parent),
97             m_intValue(123),
98             m_variantValue(QLatin1String("foo")),
99             m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))),
100             m_stringValue(QLatin1String("bar")),
101             m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")),
102             m_brushValue(QColor(10, 20, 30, 40)),
103             m_hiddenValue(456.0),
104             m_writeOnlyValue(789),
105             m_readOnlyValue(987),
106             m_qtFunctionInvoked(-1) { }
107
108     ~MyQObject() { }
109
110     int intProperty() const {
111         return m_intValue;
112     }
113     void setIntProperty(int value) {
114         m_intValue = value;
115     }
116
117     QVariant variantProperty() const {
118         return m_variantValue;
119     }
120     void setVariantProperty(const QVariant &value) {
121         m_variantValue = value;
122     }
123
124     QVariantList variantListProperty() const {
125         return m_variantListValue;
126     }
127     void setVariantListProperty(const QVariantList &value) {
128         m_variantListValue = value;
129     }
130
131     QString stringProperty() const {
132         return m_stringValue;
133     }
134     void setStringProperty(const QString &value) {
135         m_stringValue = value;
136     }
137
138     QStringList stringListProperty() const {
139         return m_stringListValue;
140     }
141     void setStringListProperty(const QStringList &value) {
142         m_stringListValue = value;
143     }
144
145     QByteArray byteArrayProperty() const {
146         return m_byteArrayValue;
147     }
148     void setByteArrayProperty(const QByteArray &value) {
149         m_byteArrayValue = value;
150     }
151
152     QBrush brushProperty() const {
153         return m_brushValue;
154     }
155     Q_INVOKABLE void setBrushProperty(const QBrush &value) {
156         m_brushValue = value;
157     }
158
159     double hiddenProperty() const {
160         return m_hiddenValue;
161     }
162     void setHiddenProperty(double value) {
163         m_hiddenValue = value;
164     }
165
166     int writeOnlyProperty() const {
167         return m_writeOnlyValue;
168     }
169     void setWriteOnlyProperty(int value) {
170         m_writeOnlyValue = value;
171     }
172
173     int readOnlyProperty() const {
174         return m_readOnlyValue;
175     }
176
177     QKeySequence shortcut() const {
178         return m_shortcut;
179     }
180     void setShortcut(const QKeySequence &seq) {
181         m_shortcut = seq;
182     }
183
184     CustomType propWithCustomType() const {
185         return m_customType;
186     }
187     void setPropWithCustomType(const CustomType &c) {
188         m_customType = c;
189     }
190
191     int qtFunctionInvoked() const {
192         return m_qtFunctionInvoked;
193     }
194
195     QVariantList qtFunctionActuals() const {
196         return m_actuals;
197     }
198
199     void resetQtFunctionInvoked() {
200         m_qtFunctionInvoked = -1;
201         m_actuals.clear();
202     }
203
204     Q_INVOKABLE void myInvokable() {
205         m_qtFunctionInvoked = 0;
206     }
207     Q_INVOKABLE void myInvokableWithIntArg(int arg) {
208         m_qtFunctionInvoked = 1;
209         m_actuals << arg;
210     }
211     Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) {
212         m_qtFunctionInvoked = 2;
213         m_actuals << arg;
214     }
215     Q_INVOKABLE void myInvokableWithFloatArg(float arg) {
216         m_qtFunctionInvoked = 3;
217         m_actuals << arg;
218     }
219     Q_INVOKABLE void myInvokableWithDoubleArg(double arg) {
220         m_qtFunctionInvoked = 4;
221         m_actuals << arg;
222     }
223     Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) {
224         m_qtFunctionInvoked = 5;
225         m_actuals << arg;
226     }
227     Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) {
228         m_qtFunctionInvoked = 6;
229         m_actuals << arg1 << arg2;
230     }
231     Q_INVOKABLE int myInvokableReturningInt() {
232         m_qtFunctionInvoked = 7;
233         return 123;
234     }
235     Q_INVOKABLE qlonglong myInvokableReturningLongLong() {
236         m_qtFunctionInvoked = 39;
237         return 456;
238     }
239     Q_INVOKABLE QString myInvokableReturningString() {
240         m_qtFunctionInvoked = 8;
241         return QLatin1String("ciao");
242     }
243     Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload
244         m_qtFunctionInvoked = 9;
245         m_actuals << arg1 << arg2;
246     }
247     Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) {
248         m_qtFunctionInvoked = 10;
249         m_actuals << policy;
250     }
251     Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) {
252         m_qtFunctionInvoked = 36;
253         m_actuals << policy;
254     }
255     Q_INVOKABLE Policy myInvokableReturningEnum() {
256         m_qtFunctionInvoked = 37;
257         return BazPolicy;
258     }
259     Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() {
260         m_qtFunctionInvoked = 38;
261         return BazPolicy;
262     }
263     Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() {
264         m_qtFunctionInvoked = 11;
265         return QVector<int>();
266     }
267     Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) {
268         m_qtFunctionInvoked = 12;
269     }
270     Q_INVOKABLE QObject* myInvokableReturningQObjectStar() {
271         m_qtFunctionInvoked = 13;
272         return this;
273     }
274     Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) {
275         m_qtFunctionInvoked = 14;
276         m_actuals << qVariantFromValue(lst);
277         return lst;
278     }
279     Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) {
280         m_qtFunctionInvoked = 15;
281         m_actuals << v;
282         return v;
283     }
284     Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) {
285         m_qtFunctionInvoked = 16;
286         m_actuals << vm;
287         return vm;
288     }
289     Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) {
290         m_qtFunctionInvoked = 17;
291         m_actuals << qVariantFromValue(lst);
292         return lst;
293     }
294     Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) {
295         m_qtFunctionInvoked = 18;
296         m_actuals << qVariantFromValue(obj);
297         return obj;
298     }
299     Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) {
300         m_qtFunctionInvoked = 19;
301         m_actuals << qVariantFromValue(brush);
302         return brush;
303     }
304     Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) {
305         m_qtFunctionInvoked = 43;
306         m_actuals << qVariantFromValue(style);
307     }
308     Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) {
309         m_qtFunctionInvoked = 44;
310         m_actuals << qVariantFromValue(arg);
311     }
312     Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) {
313         m_qtFunctionInvoked = 45;
314         m_actuals << qVariantFromValue(arg);
315     }
316     Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) {
317         m_qtFunctionInvoked = 46;
318         m_actuals << qVariantFromValue(arg);
319     }
320     Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") {
321         m_qtFunctionInvoked = 47;
322         m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2);
323     }
324     Q_INVOKABLE QObject& myInvokableReturningRef() {
325         m_qtFunctionInvoked = 48;
326         return *this;
327     }
328     Q_INVOKABLE const QObject& myInvokableReturningConstRef() const {
329         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49;
330         return *this;
331     }
332     Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) {
333         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50;
334         m_actuals << qVariantFromValue(arg);
335     }
336     Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) {
337         const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51;
338         m_actuals << qVariantFromValue(arg);
339     }
340     Q_INVOKABLE void myInvokableWithBoolArg(bool arg) {
341         m_qtFunctionInvoked = 52;
342         m_actuals << arg;
343     }
344
345     void emitMySignal() {
346         emit mySignal();
347     }
348     void emitMySignalWithIntArg(int arg) {
349         emit mySignalWithIntArg(arg);
350     }
351     void emitMySignal2(bool arg) {
352         emit mySignal2(arg);
353     }
354     void emitMySignal2() {
355         emit mySignal2();
356     }
357     void emitMySignalWithDateTimeArg(QDateTime dt) {
358         emit mySignalWithDateTimeArg(dt);
359     }
360     void emitMySignalWithRegexArg(QRegExp r) {
361         emit mySignalWithRegexArg(r);
362     }
363
364 public Q_SLOTS:
365     void mySlot() {
366         m_qtFunctionInvoked = 20;
367     }
368     void mySlotWithIntArg(int arg) {
369         m_qtFunctionInvoked = 21;
370         m_actuals << arg;
371     }
372     void mySlotWithDoubleArg(double arg) {
373         m_qtFunctionInvoked = 22;
374         m_actuals << arg;
375     }
376     void mySlotWithStringArg(const QString &arg) {
377         m_qtFunctionInvoked = 23;
378         m_actuals << arg;
379     }
380
381     void myOverloadedSlot() {
382         m_qtFunctionInvoked = 24;
383     }
384     void myOverloadedSlot(QObject* arg) {
385         m_qtFunctionInvoked = 41;
386         m_actuals << qVariantFromValue(arg);
387     }
388     void myOverloadedSlot(bool arg) {
389         m_qtFunctionInvoked = 25;
390         m_actuals << arg;
391     }
392     void myOverloadedSlot(const QStringList &arg) {
393         m_qtFunctionInvoked = 42;
394         m_actuals << arg;
395     }
396     void myOverloadedSlot(double arg) {
397         m_qtFunctionInvoked = 26;
398         m_actuals << arg;
399     }
400     void myOverloadedSlot(float arg) {
401         m_qtFunctionInvoked = 27;
402         m_actuals << arg;
403     }
404     void myOverloadedSlot(int arg) {
405         m_qtFunctionInvoked = 28;
406         m_actuals << arg;
407     }
408     void myOverloadedSlot(const QString &arg) {
409         m_qtFunctionInvoked = 29;
410         m_actuals << arg;
411     }
412     void myOverloadedSlot(const QColor &arg) {
413         m_qtFunctionInvoked = 30;
414         m_actuals << arg;
415     }
416     void myOverloadedSlot(const QBrush &arg) {
417         m_qtFunctionInvoked = 31;
418         m_actuals << arg;
419     }
420     void myOverloadedSlot(const QDateTime &arg) {
421         m_qtFunctionInvoked = 32;
422         m_actuals << arg;
423     }
424     void myOverloadedSlot(const QDate &arg) {
425         m_qtFunctionInvoked = 33;
426         m_actuals << arg;
427     }
428     void myOverloadedSlot(const QRegExp &arg) {
429         m_qtFunctionInvoked = 34;
430         m_actuals << arg;
431     }
432     void myOverloadedSlot(const QVariant &arg) {
433         m_qtFunctionInvoked = 35;
434         m_actuals << arg;
435     }
436
437     void qscript_call(int arg) {
438         m_qtFunctionInvoked = 40;
439         m_actuals << arg;
440     }
441
442 protected Q_SLOTS:
443     void myProtectedSlot() {
444         m_qtFunctionInvoked = 36;
445     }
446
447 private Q_SLOTS:
448     void myPrivateSlot() { }
449
450 Q_SIGNALS:
451     void mySignal();
452     void mySignalWithIntArg(int arg);
453     void mySignalWithDoubleArg(double arg);
454     void mySignal2(bool arg = false);
455     void mySignalWithDateTimeArg(QDateTime dt);
456     void mySignalWithRegexArg(QRegExp r);
457
458 private:
459     int m_intValue;
460     QVariant m_variantValue;
461     QVariantList m_variantListValue;
462     QString m_stringValue;
463     QStringList m_stringListValue;
464     QByteArray m_byteArrayValue;
465     QBrush m_brushValue;
466     double m_hiddenValue;
467     int m_writeOnlyValue;
468     int m_readOnlyValue;
469     QKeySequence m_shortcut;
470     CustomType m_customType;
471     int m_qtFunctionInvoked;
472     QVariantList m_actuals;
473 };
474
475 class MyOtherQObject : public MyQObject
476 {
477 public:
478     MyOtherQObject(QObject* parent = 0)
479         : MyQObject(parent) { }
480 };
481
482 class MyEnumTestQObject : public QObject
483 {
484     Q_OBJECT
485     Q_PROPERTY(QString p1 READ p1)
486     Q_PROPERTY(QString p2 READ p2)
487     Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false)
488     Q_PROPERTY(QString p4 READ p4)
489     Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false)
490     Q_PROPERTY(QString p6 READ p6)
491 public:
492     MyEnumTestQObject(QObject* parent = 0)
493         : QObject(parent) { }
494     QString p1() const {
495         return QLatin1String("p1");
496     }
497     QString p2() const {
498         return QLatin1String("p2");
499     }
500     QString p3() const {
501         return QLatin1String("p3");
502     }
503     QString p4() const {
504         return QLatin1String("p4");
505     }
506     QString p5() const {
507         return QLatin1String("p5");
508     }
509     QString p6() const {
510         return QLatin1String("p5");
511     }
512 public Q_SLOTS:
513     void mySlot() { }
514     void myOtherSlot() { }
515 Q_SIGNALS:
516     void mySignal();
517 };
518
519 class tst_QWebFrame : public QObject
520 {
521     Q_OBJECT
522
523 public:
524     tst_QWebFrame();
525     virtual ~tst_QWebFrame();
526     bool eventFilter(QObject* watched, QEvent* event);
527
528 public slots:
529     void init();
530     void cleanup();
531
532 private slots:
533     void getSetStaticProperty();
534     void getSetDynamicProperty();
535     void getSetChildren();
536     void callQtInvokable();
537     void connectAndDisconnect();
538     void classEnums();
539     void classConstructor();
540     void overrideInvokable();
541     void transferInvokable();
542     void findChild();
543     void findChildren();
544     void overloadedSlots();
545     void enumerate_data();
546     void enumerate();
547     void objectDeleted();
548     void typeConversion();
549     void arrayObjectEnumerable();
550     void symmetricUrl();
551     void progressSignal();
552     void urlChange();
553     void domCycles();
554     void requestedUrl();
555     void javaScriptWindowObjectCleared_data();
556     void javaScriptWindowObjectCleared();
557     void javaScriptWindowObjectClearedOnEvaluate();
558     void setHtml();
559     void setHtmlWithResource();
560     void setHtmlWithBaseURL();
561     void ipv6HostEncoding();
562     void metaData();
563     void popupFocus();
564     void hitTestContent();
565     void jsByteArray();
566     void ownership();
567     void nullValue();
568     void baseUrl_data();
569     void baseUrl();
570     void hasSetFocus();
571     void render();
572     void scrollPosition();
573     void evaluateWillCauseRepaint();
574     void qObjectWrapperWithSameIdentity();
575     void scrollRecursively();
576     void introspectQtMethods_data();
577     void introspectQtMethods();
578
579 private:
580     QString  evalJS(const QString&s) {
581         // Convert an undefined return variant to the string "undefined"
582         QVariant ret = evalJSV(s);
583         if (ret.userType() == QMetaType::Void)
584             return "undefined";
585         else
586             return ret.toString();
587     }
588     QVariant evalJSV(const QString &s) {
589         return m_page->mainFrame()->evaluateJavaScript(s);
590     }
591
592     QString  evalJS(const QString&s, QString& type) {
593         return evalJSV(s, type).toString();
594     }
595     QVariant evalJSV(const QString &s, QString& type) {
596         // As a special measure, if we get an exception we set the type to 'error'
597         // (in ecma, an Error object has typeof object, but qtscript has a convenience function)
598         // Similarly, an array is an object, but we'd prefer to have a type of 'array'
599         // Also, consider a QMetaType::Void QVariant to be undefined
600         QString escaped = s;
601         escaped.replace('\'', "\\'"); // Don't preescape your single quotes!
602         evalJS("var retvalue;\
603                var typevalue; \
604                try {\
605                retvalue = eval('" + escaped + "'); \
606                typevalue = typeof retvalue; \
607                if (retvalue instanceof Array) \
608                typevalue = 'array'; \
609            } \
610                catch(e) {\
611                retvalue = e.name + ': ' + e.message;\
612                typevalue = 'error';\
613            }");
614         QVariant ret = evalJSV("retvalue");
615         if (ret.userType() != QMetaType::Void)
616             type = evalJS("typevalue");
617         else {
618             ret = QString("undefined");
619             type = sUndefined;
620         }
621         evalJS("delete retvalue; delete typevalue");
622         return ret;
623     }
624     QObject* firstChildByClassName(QObject* parent, const char* className) {
625         const QObjectList & children = parent->children();
626         foreach (QObject* child, children) {
627             if (!strcmp(child->metaObject()->className(), className)) {
628                 return child;
629             }
630         }
631         return 0;
632     }
633
634     const QString sTrue;
635     const QString sFalse;
636     const QString sUndefined;
637     const QString sArray;
638     const QString sFunction;
639     const QString sError;
640     const QString sString;
641     const QString sObject;
642     const QString sNumber;
643
644 private:
645     QWebView* m_view;
646     QWebPage* m_page;
647     MyQObject* m_myObject;
648     QWebView* m_popupTestView;
649     int m_popupTestPaintCount;
650 };
651
652 tst_QWebFrame::tst_QWebFrame()
653     : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"),
654         sString("string"), sObject("object"), sNumber("number"), m_popupTestView(0), m_popupTestPaintCount(0)
655 {
656 }
657
658 tst_QWebFrame::~tst_QWebFrame()
659 {
660 }
661
662 bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event)
663 {
664     // used on the popupFocus test
665     if (watched == m_popupTestView) {
666         if (event->type() == QEvent::Paint)
667             m_popupTestPaintCount++;
668     }
669     return QObject::eventFilter(watched, event);
670 }
671
672 void tst_QWebFrame::init()
673 {
674     m_view = new QWebView();
675     m_page = m_view->page();
676     m_myObject = new MyQObject();
677     m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject);
678 }
679
680 void tst_QWebFrame::cleanup()
681 {
682     delete m_view;
683     delete m_myObject;
684 }
685
686 void tst_QWebFrame::getSetStaticProperty()
687 {
688     QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined);
689
690     // initial value (set in MyQObject constructor)
691     {
692         QString type;
693         QVariant ret = evalJSV("myObject.intProperty", type);
694         QCOMPARE(type, sNumber);
695         QCOMPARE(ret.type(), QVariant::Double);
696         QCOMPARE(ret.toInt(), 123);
697     }
698     QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue);
699
700     {
701         QString type;
702         QVariant ret = evalJSV("myObject.variantProperty", type);
703         QCOMPARE(type, sString);
704         QCOMPARE(ret.type(), QVariant::String);
705         QCOMPARE(ret.toString(), QLatin1String("foo"));
706     }
707     QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue);
708
709     {
710         QString type;
711         QVariant ret = evalJSV("myObject.stringProperty", type);
712         QCOMPARE(type, sString);
713         QCOMPARE(ret.type(), QVariant::String);
714         QCOMPARE(ret.toString(), QLatin1String("bar"));
715     }
716     QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue);
717
718     {
719         QString type;
720         QVariant ret = evalJSV("myObject.variantListProperty", type);
721         QCOMPARE(type, sArray);
722         QCOMPARE(ret.type(), QVariant::List);
723         QVariantList vl = ret.value<QVariantList>();
724         QCOMPARE(vl.size(), 2);
725         QCOMPARE(vl.at(0).toInt(), 123);
726         QCOMPARE(vl.at(1).toString(), QLatin1String("foo"));
727     }
728     QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue);
729     QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue);
730     QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue);
731
732     {
733         QString type;
734         QVariant ret = evalJSV("myObject.stringListProperty", type);
735         QCOMPARE(type, sArray);
736         QCOMPARE(ret.type(), QVariant::List);
737         QVariantList vl = ret.value<QVariantList>();
738         QCOMPARE(vl.size(), 2);
739         QCOMPARE(vl.at(0).toString(), QLatin1String("zig"));
740         QCOMPARE(vl.at(1).toString(), QLatin1String("zag"));
741     }
742     QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue);
743     QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
744     QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig"));
745     QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
746     QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag"));
747
748     // property change in C++ should be reflected in script
749     m_myObject->setIntProperty(456);
750     QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue);
751     m_myObject->setIntProperty(789);
752     QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue);
753
754     m_myObject->setVariantProperty(QLatin1String("bar"));
755     QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue);
756     m_myObject->setVariantProperty(42);
757     QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue);
758     m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
759 //XFAIL
760 //  QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant);
761
762     m_myObject->setStringProperty(QLatin1String("baz"));
763     QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue);
764     m_myObject->setStringProperty(QLatin1String("zab"));
765     QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue);
766
767     // property change in script should be reflected in C++
768     QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123"));
769     QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
770     QCOMPARE(m_myObject->intProperty(), 123);
771     QCOMPARE(evalJS("myObject.intProperty = 'ciao!';"
772                     "myObject.intProperty == 0"), sTrue);
773     QCOMPARE(m_myObject->intProperty(), 0);
774     QCOMPARE(evalJS("myObject.intProperty = '123';"
775                     "myObject.intProperty == 123"), sTrue);
776     QCOMPARE(m_myObject->intProperty(), 123);
777
778     QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao"));
779     QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao"));
780     QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao"));
781     QCOMPARE(evalJS("myObject.stringProperty = 123;"
782                     "myObject.stringProperty"), QLatin1String("123"));
783     QCOMPARE(m_myObject->stringProperty(), QLatin1String("123"));
784     QCOMPARE(evalJS("myObject.stringProperty = null"), QString());
785     QCOMPARE(evalJS("myObject.stringProperty"), QString());
786     QCOMPARE(m_myObject->stringProperty(), QString());
787     QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined);
788     QCOMPARE(evalJS("myObject.stringProperty"), QString());
789     QCOMPARE(m_myObject->stringProperty(), QString());
790
791     QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);"
792                     "myObject.variantProperty").toDouble(), 1234.0);
793     QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0);
794
795     QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);"
796                     "myObject.variantProperty"), sTrue);
797     QCOMPARE(m_myObject->variantProperty().toBool(), true);
798
799     QCOMPARE(evalJS("myObject.variantProperty = null;"
800                     "myObject.variantProperty.valueOf()"), sUndefined);
801     QCOMPARE(m_myObject->variantProperty(), QVariant());
802     QCOMPARE(evalJS("myObject.variantProperty = undefined;"
803                     "myObject.variantProperty.valueOf()"), sUndefined);
804     QCOMPARE(m_myObject->variantProperty(), QVariant());
805
806     QCOMPARE(evalJS("myObject.variantProperty = 'foo';"
807                     "myObject.variantProperty.valueOf()"), QLatin1String("foo"));
808     QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo")));
809     QCOMPARE(evalJS("myObject.variantProperty = 42;"
810                     "myObject.variantProperty").toDouble(), 42.0);
811     QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0);
812
813     QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];"
814                     "myObject.variantListProperty.length == 3"), sTrue);
815     QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue);
816     QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two"));
817     QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue);
818
819     QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];"
820                     "myObject.stringListProperty.length == 3"), sTrue);
821     QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString);
822     QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue);
823     QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString);
824     QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two"));
825     QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString);
826     QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true"));
827
828     // try to delete
829     QCOMPARE(evalJS("delete myObject.intProperty"), sFalse);
830     QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue);
831
832     QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse);
833     QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0);
834
835     // custom property
836     QCOMPARE(evalJS("myObject.customProperty"), sUndefined);
837     QCOMPARE(evalJS("myObject.customProperty = 123;"
838                     "myObject.customProperty == 123"), sTrue);
839     QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty");
840     QCOMPARE(v.type(), QVariant::Double);
841     QCOMPARE(v.toInt(), 123);
842
843     // non-scriptable property
844     QCOMPARE(m_myObject->hiddenProperty(), 456.0);
845     QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined);
846     QCOMPARE(evalJS("myObject.hiddenProperty = 123;"
847                     "myObject.hiddenProperty == 123"), sTrue);
848     QCOMPARE(m_myObject->hiddenProperty(), 456.0);
849
850     // write-only property
851     QCOMPARE(m_myObject->writeOnlyProperty(), 789);
852     QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined);
853     QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;"
854                     "typeof myObject.writeOnlyProperty"), sUndefined);
855     QCOMPARE(m_myObject->writeOnlyProperty(), 123);
856
857     // read-only property
858     QCOMPARE(m_myObject->readOnlyProperty(), 987);
859     QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue);
860     QCOMPARE(evalJS("myObject.readOnlyProperty = 654;"
861                     "myObject.readOnlyProperty == 987"), sTrue);
862     QCOMPARE(m_myObject->readOnlyProperty(), 987);
863 }
864
865 void tst_QWebFrame::getSetDynamicProperty()
866 {
867     // initially the object does not have the property
868     // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty
869
870     //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
871     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
872
873     // add a dynamic property in C++
874     QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
875     //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue);
876     QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue);
877     QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue);
878
879     // property change in script should be reflected in C++
880     QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';"
881                     "myObject.dynamicProperty"), QLatin1String("foo"));
882     QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo"));
883
884     // delete the property (XFAIL - can't delete properties)
885     QEXPECT_FAIL("", "can't delete properties", Continue);
886     QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue);
887     /*
888     QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false);
889     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
890     //    QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse);
891     QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined);
892     */
893 }
894
895 void tst_QWebFrame::getSetChildren()
896 {
897     // initially the object does not have the child
898     // (again, no hasOwnProperty)
899
900     //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
901     QCOMPARE(evalJS("typeof myObject.child"), sUndefined);
902
903     // add a child
904     MyQObject* child = new MyQObject(m_myObject);
905     child->setObjectName("child");
906 //  QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue);
907     QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue);
908
909     // add a grandchild
910     MyQObject* grandChild = new MyQObject(child);
911     grandChild->setObjectName("grandChild");
912 //  QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue);
913     QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue);
914
915     // delete grandchild
916     delete grandChild;
917 //  QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse);
918     QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue);
919
920     // delete child
921     delete child;
922 //  QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse);
923     QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue);
924 }
925
926 Q_DECLARE_METATYPE(QVector<int>)
927 Q_DECLARE_METATYPE(QVector<double>)
928 Q_DECLARE_METATYPE(QVector<QString>)
929
930 void tst_QWebFrame::callQtInvokable()
931 {
932     qRegisterMetaType<QObjectList>();
933
934     m_myObject->resetQtFunctionInvoked();
935     QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined);
936     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
937     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
938
939     // extra arguments should silently be ignored
940     m_myObject->resetQtFunctionInvoked();
941     QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined);
942     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
943     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
944
945     m_myObject->resetQtFunctionInvoked();
946     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined);
947     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
948     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
949     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
950
951     m_myObject->resetQtFunctionInvoked();
952     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined);
953     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
954     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
955     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
956
957     m_myObject->resetQtFunctionInvoked();
958     QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined);
959     QCOMPARE(m_myObject->qtFunctionInvoked(), 2);
960     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
961     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123));
962
963     m_myObject->resetQtFunctionInvoked();
964     QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined);
965     QCOMPARE(m_myObject->qtFunctionInvoked(), 3);
966     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
967     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
968
969     m_myObject->resetQtFunctionInvoked();
970     QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined);
971     QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
972     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
973     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
974
975     m_myObject->resetQtFunctionInvoked();
976     QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined);
977     QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
978     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
979     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5);
980
981     m_myObject->resetQtFunctionInvoked();
982     QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined);
983     QCOMPARE(m_myObject->qtFunctionInvoked(), 52);
984     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
985     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true);
986
987     m_myObject->resetQtFunctionInvoked();
988     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined);
989     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
990     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
991     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao"));
992
993     m_myObject->resetQtFunctionInvoked();
994     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined);
995     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
996     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
997     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
998
999     m_myObject->resetQtFunctionInvoked();
1000     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined);
1001     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1002     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1003     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1004     QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1005
1006     m_myObject->resetQtFunctionInvoked();
1007     QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined);
1008     QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
1009     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1010     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
1011     QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty());
1012
1013     m_myObject->resetQtFunctionInvoked();
1014     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined);
1015     QCOMPARE(m_myObject->qtFunctionInvoked(), 6);
1016     QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1017     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1018     QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1019
1020     m_myObject->resetQtFunctionInvoked();
1021     QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123"));
1022     QCOMPARE(m_myObject->qtFunctionInvoked(), 7);
1023     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1024
1025     m_myObject->resetQtFunctionInvoked();
1026     QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456"));
1027     QCOMPARE(m_myObject->qtFunctionInvoked(), 39);
1028     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1029
1030     m_myObject->resetQtFunctionInvoked();
1031     QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao"));
1032     QCOMPARE(m_myObject->qtFunctionInvoked(), 8);
1033     QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
1034
1035     m_myObject->resetQtFunctionInvoked();
1036     QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined);
1037     QCOMPARE(m_myObject->qtFunctionInvoked(), 9);
1038     QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1039     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1040     QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
1041
1042     m_myObject->resetQtFunctionInvoked();
1043     QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined);
1044     QCOMPARE(m_myObject->qtFunctionInvoked(), 44);
1045     m_myObject->resetQtFunctionInvoked();
1046     {
1047         QString type;
1048         QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type);
1049         QCOMPARE(type, sError);
1050         QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n    myInvokableWithVoidStarArg(void*)"));
1051         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1052     }
1053
1054     m_myObject->resetQtFunctionInvoked();
1055     {
1056         QString type;
1057         QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type);
1058         QCOMPARE(type, sError);
1059         QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n    myInvokableWithAmbiguousArg(int)\n    myInvokableWithAmbiguousArg(uint)"));
1060     }
1061
1062     m_myObject->resetQtFunctionInvoked();
1063     {
1064         QString type;
1065         QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type);
1066         QCOMPARE(type, sUndefined);
1067         QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1068         QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1069         QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1070         QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello"));
1071     }
1072
1073     m_myObject->resetQtFunctionInvoked();
1074     {
1075         QString type;
1076         QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type);
1077         QCOMPARE(type, sUndefined);
1078         QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
1079         QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
1080         QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1081         QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString());
1082     }
1083
1084     // calling function that returns (const)ref
1085     m_myObject->resetQtFunctionInvoked();
1086     {
1087         QString type;
1088         QString ret = evalJS("typeof myObject.myInvokableReturningRef()");
1089         QCOMPARE(ret, sUndefined);
1090         //QVERIFY(!m_engine->hasUncaughtException());
1091         QCOMPARE(m_myObject->qtFunctionInvoked(), 48);
1092     }
1093
1094     m_myObject->resetQtFunctionInvoked();
1095     {
1096         QString type;
1097         QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()");
1098         QCOMPARE(ret, sUndefined);
1099         //QVERIFY(!m_engine->hasUncaughtException());
1100         QCOMPARE(m_myObject->qtFunctionInvoked(), 49);
1101     }
1102
1103     m_myObject->resetQtFunctionInvoked();
1104     {
1105         QString type;
1106         QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type);
1107         QCOMPARE(m_myObject->qtFunctionInvoked(), 13);
1108         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1109         QCOMPARE(type, sObject);
1110         QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1111     }
1112
1113     m_myObject->resetQtFunctionInvoked();
1114     {
1115         QString type;
1116         QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type);
1117         QCOMPARE(m_myObject->qtFunctionInvoked(), 14);
1118         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1119         QCOMPARE(type, sArray);
1120         QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList
1121         QVariantList vl = qvariant_cast<QVariantList>(ret);
1122         QCOMPARE(vl.count(), 1);
1123     }
1124
1125     m_myObject->resetQtFunctionInvoked();
1126     {
1127         QString type;
1128         m_myObject->setVariantProperty(QVariant(123));
1129         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type);
1130         QCOMPARE(type, sNumber);
1131         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1132         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1133         QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty());
1134         QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int
1135         QCOMPARE(ret.toInt(),123);
1136     }
1137
1138     m_myObject->resetQtFunctionInvoked();
1139     {
1140         QString type;
1141         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type);
1142         QCOMPARE(type, sObject);
1143         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1144         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1145         QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1146         QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1147     }
1148
1149     m_myObject->resetQtFunctionInvoked();
1150     {
1151         QString type;
1152         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type);
1153         QCOMPARE(type, sObject);
1154         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1155         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1156         QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
1157         QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid());
1158     }
1159
1160     /* XFAIL - variant support
1161     m_myObject->resetQtFunctionInvoked();
1162     {
1163         m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
1164         QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
1165         QVERIFY(ret.isVariant());
1166         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1167         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1168         QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0));
1169         QCOMPARE(ret.toVariant(), m_myObject->variantProperty());
1170     }
1171     */
1172
1173     m_myObject->resetQtFunctionInvoked();
1174     {
1175         QString type;
1176         QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type);
1177         QCOMPARE(type, sNumber);
1178         QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
1179         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1180         QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123));
1181         QCOMPARE(ret.userType(), int(QMetaType::Double));
1182         QCOMPARE(ret.toInt(),123);
1183     }
1184
1185     m_myObject->resetQtFunctionInvoked();
1186     {
1187         QString type;
1188         QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type);
1189         QCOMPARE(m_myObject->qtFunctionInvoked(), 16);
1190         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1191
1192         QVariant v = m_myObject->qtFunctionActuals().at(0);
1193         QCOMPARE(v.userType(), int(QMetaType::QVariantMap));
1194
1195         QVariantMap vmap = qvariant_cast<QVariantMap>(v);
1196         QCOMPARE(vmap.keys().size(), 2);
1197         QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1198         QCOMPARE(vmap.value("a"), QVariant(123));
1199         QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1200         QCOMPARE(vmap.value("b"), QVariant("ciao"));
1201
1202         QCOMPARE(type, sObject);
1203
1204         QCOMPARE(ret.userType(), int(QMetaType::QVariantMap));
1205         vmap = qvariant_cast<QVariantMap>(ret);
1206         QCOMPARE(vmap.keys().size(), 2);
1207         QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
1208         QCOMPARE(vmap.value("a"), QVariant(123));
1209         QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
1210         QCOMPARE(vmap.value("b"), QVariant("ciao"));
1211     }
1212
1213     m_myObject->resetQtFunctionInvoked();
1214     {
1215         QString type;
1216         QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type);
1217         QCOMPARE(m_myObject->qtFunctionInvoked(), 17);
1218         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1219         QVariant v = m_myObject->qtFunctionActuals().at(0);
1220         QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
1221         QList<int> ilst = qvariant_cast<QList<int> >(v);
1222         QCOMPARE(ilst.size(), 2);
1223         QCOMPARE(ilst.at(0), 1);
1224         QCOMPARE(ilst.at(1), 5);
1225
1226         QCOMPARE(type, sArray);
1227         QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist
1228         QVariantList vlst = qvariant_cast<QVariantList>(ret);
1229         QCOMPARE(vlst.size(), 2);
1230         QCOMPARE(vlst.at(0).toInt(), 1);
1231         QCOMPARE(vlst.at(1).toInt(), 5);
1232     }
1233
1234     m_myObject->resetQtFunctionInvoked();
1235     {
1236         QString type;
1237         QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type);
1238         QCOMPARE(m_myObject->qtFunctionInvoked(), 18);
1239         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1240         QVariant v = m_myObject->qtFunctionActuals().at(0);
1241         QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1242         QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject);
1243
1244         QCOMPARE(ret.userType(), int(QMetaType::QObjectStar));
1245         QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject);
1246
1247         QCOMPARE(type, sObject);
1248     }
1249
1250     m_myObject->resetQtFunctionInvoked();
1251     {
1252         // no implicit conversion from integer to QObject*
1253         QString type;
1254         evalJS("myObject.myInvokableWithQObjectStarArg(123)", type);
1255         QCOMPARE(type, sError);
1256     }
1257
1258     /*
1259     m_myObject->resetQtFunctionInvoked();
1260     {
1261         QString fun = evalJS("myObject.myInvokableWithQBrushArg");
1262         Q_ASSERT(fun.isFunction());
1263         QColor color(10, 20, 30, 40);
1264         // QColor should be converted to a QBrush
1265         QVariant ret = fun.call(QString(), QStringList()
1266                                     << qScriptValueFromValue(m_engine, color));
1267         QCOMPARE(m_myObject->qtFunctionInvoked(), 19);
1268         QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1269         QVariant v = m_myObject->qtFunctionActuals().at(0);
1270         QCOMPARE(v.userType(), int(QMetaType::QBrush));
1271         QCOMPARE(qvariant_cast<QColor>(v), color);
1272
1273         QCOMPARE(qscriptvalue_cast<QColor>(ret), color);
1274     }
1275     */
1276
1277     // private slots should not be part of the QObject binding
1278     QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined);
1279
1280     // protected slots should be fine
1281     m_myObject->resetQtFunctionInvoked();
1282     evalJS("myObject.myProtectedSlot()");
1283     QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1284
1285     // call with too few arguments
1286     {
1287         QString type;
1288         QString ret = evalJS("myObject.myInvokableWithIntArg()", type);
1289         QCOMPARE(type, sError);
1290         QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n    myInvokableWithIntArg(int,int)\n    myInvokableWithIntArg(int)"));
1291     }
1292
1293     // call function where not all types have been registered
1294     m_myObject->resetQtFunctionInvoked();
1295     {
1296         QString type;
1297         QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type);
1298         QCOMPARE(type, sError);
1299         QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'"));
1300         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1301     }
1302
1303     // call function with incompatible argument type
1304     m_myObject->resetQtFunctionInvoked();
1305     {
1306         QString type;
1307         QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type);
1308         QCOMPARE(type, sError);
1309         QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n    myInvokableWithQBrushArg(QBrush)"));
1310         QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1311     }
1312 }
1313
1314 void tst_QWebFrame::connectAndDisconnect()
1315 {
1316     // connect(function)
1317     QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction);
1318     QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction);
1319     QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction);
1320
1321     {
1322         QString type;
1323         evalJS("myObject.mySignal.connect(123)", type);
1324         QCOMPARE(type, sError);
1325     }
1326
1327     evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }");
1328
1329     QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined);
1330
1331     evalJS("gotSignal = false");
1332     evalJS("myObject.mySignal()");
1333     QCOMPARE(evalJS("gotSignal"), sTrue);
1334     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1335     QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1336     QCOMPARE(evalJS("slotThisObject == window"), sTrue);
1337
1338     evalJS("gotSignal = false");
1339     m_myObject->emitMySignal();
1340     QCOMPARE(evalJS("gotSignal"), sTrue);
1341     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1342
1343     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined);
1344
1345     evalJS("gotSignal = false");
1346     m_myObject->emitMySignalWithIntArg(123);
1347     QCOMPARE(evalJS("gotSignal"), sTrue);
1348     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1349     QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue);
1350
1351     QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined);
1352     {
1353         QString type;
1354         evalJS("myObject.mySignal.disconnect(myHandler)", type);
1355         QCOMPARE(type, sError);
1356     }
1357
1358     evalJS("gotSignal = false");
1359     QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined);
1360     m_myObject->emitMySignal2(true);
1361     QCOMPARE(evalJS("gotSignal"), sTrue);
1362     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1363     QCOMPARE(evalJS("signalArgs[0]"), sTrue);
1364
1365     QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined);
1366
1367     QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction);
1368     QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction);
1369     QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction);
1370
1371     QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined);
1372
1373     evalJS("gotSignal = false");
1374     m_myObject->emitMySignal2();
1375     QCOMPARE(evalJS("gotSignal"), sTrue);
1376
1377     QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined);
1378
1379     // connect(object, function)
1380     evalJS("otherObject = { name:'foo' }");
1381     QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1382     QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1383     evalJS("gotSignal = false");
1384     m_myObject->emitMySignal();
1385     QCOMPARE(evalJS("gotSignal"), sFalse);
1386
1387     {
1388         QString type;
1389         evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type);
1390         QCOMPARE(type, sError);
1391     }
1392
1393     QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined);
1394     evalJS("gotSignal = false");
1395     m_myObject->emitMySignal();
1396     QCOMPARE(evalJS("gotSignal"), sTrue);
1397     QCOMPARE(evalJS("signalArgs.length == 0"), sTrue);
1398     QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject"));
1399     QCOMPARE(evalJS("signalSender"),evalJS("myObject"));
1400     QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo"));
1401     QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined);
1402
1403     evalJS("yetAnotherObject = { name:'bar', func : function() { } }");
1404     QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined);
1405     evalJS("gotSignal = false");
1406     m_myObject->emitMySignal2(true);
1407     QCOMPARE(evalJS("gotSignal"), sTrue);
1408     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1409     QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue);
1410     QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1411     QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar"));
1412     QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined);
1413
1414     QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined);
1415     evalJS("gotSignal = false");
1416     m_myObject->emitMySignal2(true);
1417     QCOMPARE(evalJS("gotSignal"), sTrue);
1418     QCOMPARE(evalJS("signalArgs.length == 1"), sTrue);
1419     QCOMPARE(evalJS("slotThisObject == myObject"), sTrue);
1420     QCOMPARE(evalJS("signalSender == myObject"), sTrue);
1421     QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined);
1422
1423     // connect(obj, string)
1424     QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined);
1425     QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined);
1426     QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined);
1427     QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined);
1428
1429     // check that emitting signals from script works
1430
1431     // no arguments
1432     QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1433     m_myObject->resetQtFunctionInvoked();
1434     QCOMPARE(evalJS("myObject.mySignal()"), sUndefined);
1435     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1436     QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined);
1437
1438     // one argument
1439     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined);
1440     m_myObject->resetQtFunctionInvoked();
1441     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1442     QCOMPARE(m_myObject->qtFunctionInvoked(), 21);
1443     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1444     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1445     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined);
1446
1447     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined);
1448     m_myObject->resetQtFunctionInvoked();
1449     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1450     QCOMPARE(m_myObject->qtFunctionInvoked(), 22);
1451     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1452     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0);
1453     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined);
1454
1455     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined);
1456     m_myObject->resetQtFunctionInvoked();
1457     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1458     QCOMPARE(m_myObject->qtFunctionInvoked(), 23);
1459     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1460     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
1461     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined);
1462
1463     // connecting to overloaded slot
1464     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined);
1465     m_myObject->resetQtFunctionInvoked();
1466     QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined);
1467     QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload
1468     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1469     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
1470     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined);
1471
1472     QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1473     m_myObject->resetQtFunctionInvoked();
1474     QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined);
1475     QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
1476     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1477     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
1478     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined);
1479
1480     // erroneous input
1481     {
1482         // ### QtScript adds .connect to all functions, WebKit does only to signals/slots
1483         QString type;
1484         QString ret = evalJS("(function() { }).connect()", type);
1485         QCOMPARE(type, sError);
1486         QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1487     }
1488     {
1489         QString type;
1490         QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect;  o.connect()", type);
1491         QCOMPARE(type, sError);
1492         QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1493     }
1494
1495     {
1496         QString type;
1497         QString ret = evalJS("(function() { }).connect(123)", type);
1498         QCOMPARE(type, sError);
1499         QCOMPARE(ret, QLatin1String("TypeError: Result of expression '(function() { }).connect' [undefined] is not a function."));
1500     }
1501     {
1502         QString type;
1503         QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect;  o.connect(123)", type);
1504         QCOMPARE(type, sError);
1505         QCOMPARE(ret, QLatin1String("TypeError: Result of expression 'o.connect' [undefined] is not a function."));
1506     }
1507
1508     {
1509         QString type;
1510         QString ret = evalJS("myObject.myInvokable.connect(123)", type);
1511         QCOMPARE(type, sError);
1512         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1513     }
1514     {
1515         QString type;
1516         QString ret = evalJS("myObject.myInvokable.connect(function() { })", type);
1517         QCOMPARE(type, sError);
1518         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
1519     }
1520
1521     {
1522         QString type;
1523         QString ret = evalJS("myObject.mySignal.connect(123)", type);
1524         QCOMPARE(type, sError);
1525         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function"));
1526     }
1527
1528     {
1529         QString type;
1530         QString ret = evalJS("myObject.mySignal.disconnect()", type);
1531         QCOMPARE(type, sError);
1532         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1533     }
1534     {
1535         QString type;
1536         QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect;  o.disconnect()", type);
1537         QCOMPARE(type, sError);
1538         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
1539     }
1540
1541     /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots
1542     {
1543         QString type;
1544         QString ret = evalJS("(function() { }).disconnect(123)", type);
1545         QCOMPARE(type, sError);
1546         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal"));
1547     }
1548     */
1549
1550     {
1551         QString type;
1552         QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type);
1553         QCOMPARE(type, sError);
1554         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1555     }
1556
1557     {
1558         QString type;
1559         QString ret = evalJS("myObject.myInvokable.disconnect(123)", type);
1560         QCOMPARE(type, sError);
1561         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1562     }
1563     {
1564         QString type;
1565         QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type);
1566         QCOMPARE(type, sError);
1567         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
1568     }
1569
1570     {
1571         QString type;
1572         QString ret = evalJS("myObject.mySignal.disconnect(123)", type);
1573         QCOMPARE(type, sError);
1574         QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function"));
1575     }
1576
1577     {
1578         QString type;
1579         QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type);
1580         QCOMPARE(type, sError);
1581         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()"));
1582     }
1583
1584     // when the wrapper dies, the connection stays alive
1585     QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined);
1586     m_myObject->resetQtFunctionInvoked();
1587     m_myObject->emitMySignal();
1588     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1589     evalJS("myObject = null");
1590     evalJS("gc()");
1591     m_myObject->resetQtFunctionInvoked();
1592     m_myObject->emitMySignal();
1593     QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
1594 }
1595
1596 void tst_QWebFrame::classEnums()
1597 {
1598     // We don't do the meta thing currently, unfortunately!!!
1599     /*
1600     QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue());
1601     m_engine->globalObject().setProperty("MyQObject", myClass);
1602
1603     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()),
1604              MyQObject::FooPolicy);
1605     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()),
1606              MyQObject::BarPolicy);
1607     QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()),
1608              MyQObject::BazPolicy);
1609
1610     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()),
1611              MyQObject::FooStrategy);
1612     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()),
1613              MyQObject::BarStrategy);
1614     QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()),
1615              MyQObject::BazStrategy);
1616
1617     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()),
1618              MyQObject::NoAbility);
1619     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()),
1620              MyQObject::FooAbility);
1621     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()),
1622              MyQObject::BarAbility);
1623     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()),
1624              MyQObject::BazAbility);
1625     QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()),
1626              MyQObject::AllAbility);
1627
1628     // enums from Qt are inherited through prototype
1629     QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()),
1630              Qt::StrongFocus);
1631     QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()),
1632              Qt::Key_Left);
1633
1634     QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject"));
1635
1636     qRegisterMetaType<MyQObject::Policy>("Policy");
1637
1638     m_myObject->resetQtFunctionInvoked();
1639     QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined);
1640     QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
1641     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1642     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1643
1644     m_myObject->resetQtFunctionInvoked();
1645     QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined);
1646     QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
1647     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
1648     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
1649
1650     m_myObject->resetQtFunctionInvoked();
1651     {
1652         QVariant ret = evalJS("myObject.myInvokableReturningEnum()");
1653         QCOMPARE(m_myObject->qtFunctionInvoked(), 37);
1654         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1655         QCOMPARE(ret.isVariant());
1656     }
1657     m_myObject->resetQtFunctionInvoked();
1658     {
1659         QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()");
1660         QCOMPARE(m_myObject->qtFunctionInvoked(), 38);
1661         QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
1662         QCOMPARE(ret.isNumber());
1663     }
1664     */
1665 }
1666
1667 void tst_QWebFrame::classConstructor()
1668 {
1669     /*
1670     QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine);
1671     m_engine->globalObject().setProperty("MyQObject", myClass);
1672
1673     QString myObj = evalJS("myObj = MyQObject()");
1674     QObject* qobj = myObj.toQObject();
1675     QVERIFY(qobj != 0);
1676     QCOMPARE(qobj->metaObject()->className(), "MyQObject");
1677     QCOMPARE(qobj->parent(), (QObject*)0);
1678
1679     QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine);
1680     m_engine->globalObject().setProperty("QObject", qobjectClass);
1681
1682     QString otherObj = evalJS("otherObj = QObject(myObj)");
1683     QObject* qqobj = otherObj.toQObject();
1684     QVERIFY(qqobj != 0);
1685     QCOMPARE(qqobj->metaObject()->className(), "QObject");
1686     QCOMPARE(qqobj->parent(), qobj);
1687
1688     delete qobj;
1689     */
1690 }
1691
1692 void tst_QWebFrame::overrideInvokable()
1693 {
1694     m_myObject->resetQtFunctionInvoked();
1695     QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined);
1696     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1697
1698     /* XFAIL - can't write to functions with RuntimeObject
1699     m_myObject->resetQtFunctionInvoked();
1700     evalJS("myObject.myInvokable = function() { window.a = 123; }");
1701     evalJS("myObject.myInvokable()");
1702     QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1703     QCOMPARE(evalJS("window.a").toDouble(), 123.0);
1704
1705     evalJS("myObject.myInvokable = function() { window.a = 456; }");
1706     evalJS("myObject.myInvokable()");
1707     QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
1708     QCOMPARE(evalJS("window.a").toDouble(), 456.0);
1709     */
1710
1711     evalJS("delete myObject.myInvokable");
1712     evalJS("myObject.myInvokable()");
1713     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1714
1715     /* XFAIL - ditto
1716     m_myObject->resetQtFunctionInvoked();
1717     evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1718     evalJS("myObject.myInvokable(123)");
1719     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1720     */
1721
1722     evalJS("delete myObject.myInvokable");
1723     m_myObject->resetQtFunctionInvoked();
1724     // this form (with the '()') is read-only
1725     evalJS("myObject['myInvokable()'] = function() { window.a = 123; }");
1726     evalJS("myObject.myInvokable()");
1727     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1728 }
1729
1730 void tst_QWebFrame::transferInvokable()
1731 {
1732     /* XFAIL - can't put to functions with RuntimeObject
1733     m_myObject->resetQtFunctionInvoked();
1734     evalJS("myObject.foozball = myObject.myInvokable");
1735     evalJS("myObject.foozball()");
1736     QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
1737     m_myObject->resetQtFunctionInvoked();
1738     evalJS("myObject.foozball = myObject.myInvokableWithIntArg");
1739     evalJS("myObject.foozball(123)");
1740     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1741     m_myObject->resetQtFunctionInvoked();
1742     evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg");
1743     evalJS("myObject.myInvokable(123)");
1744     QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
1745
1746     MyOtherQObject other;
1747     m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other);
1748     evalJS("myOtherObject.foo = myObject.foozball");
1749     other.resetQtFunctionInvoked();
1750     evalJS("myOtherObject.foo(456)");
1751     QCOMPARE(other.qtFunctionInvoked(), 1);
1752     */
1753 }
1754
1755 void tst_QWebFrame::findChild()
1756 {
1757     /*
1758     QObject* child = new QObject(m_myObject);
1759     child->setObjectName(QLatin1String("myChildObject"));
1760
1761     {
1762         QString result = evalJS("myObject.findChild('noSuchChild')");
1763         QCOMPARE(result.isNull());
1764     }
1765
1766     {
1767         QString result = evalJS("myObject.findChild('myChildObject')");
1768         QCOMPARE(result.isQObject());
1769         QCOMPARE(result.toQObject(), child);
1770     }
1771
1772     delete child;
1773     */
1774 }
1775
1776 void tst_QWebFrame::findChildren()
1777 {
1778     /*
1779     QObject* child = new QObject(m_myObject);
1780     child->setObjectName(QLatin1String("myChildObject"));
1781
1782     {
1783         QString result = evalJS("myObject.findChildren('noSuchChild')");
1784         QCOMPARE(result.isArray());
1785         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0);
1786     }
1787
1788     {
1789         QString result = evalJS("myObject.findChildren('myChildObject')");
1790         QCOMPARE(result.isArray());
1791         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1792         QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1793     }
1794
1795     QObject* namelessChild = new QObject(m_myObject);
1796
1797     {
1798         QString result = evalJS("myObject.findChildren('myChildObject')");
1799         QCOMPARE(result.isArray());
1800         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1801         QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
1802     }
1803
1804     QObject* anotherChild = new QObject(m_myObject);
1805     anotherChild->setObjectName(QLatin1String("anotherChildObject"));
1806
1807     {
1808         QString result = evalJS("myObject.findChildren('anotherChildObject')");
1809         QCOMPARE(result.isArray());
1810         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0);
1811         QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild);
1812     }
1813
1814     anotherChild->setObjectName(QLatin1String("myChildObject"));
1815     {
1816         QString result = evalJS("myObject.findChildren('myChildObject')");
1817         QCOMPARE(result.isArray());
1818         QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0);
1819         QObject* o1 = result.property(QLatin1String("0")).toQObject();
1820         QObject* o2 = result.property(QLatin1String("1")).toQObject();
1821         if (o1 != child) {
1822             QCOMPARE(o1, anotherChild);
1823             QCOMPARE(o2, child);
1824         } else {
1825             QCOMPARE(o1, child);
1826             QCOMPARE(o2, anotherChild);
1827         }
1828     }
1829
1830     // find all
1831     {
1832         QString result = evalJS("myObject.findChildren()");
1833         QVERIFY(result.isArray());
1834         int count = 3;
1835         QCOMPARE(result.property("length"), QLatin1String(count);
1836         for (int i = 0; i < 3; ++i) {
1837             QObject* o = result.property(i).toQObject();
1838             if (o == namelessChild || o == child || o == anotherChild)
1839                 --count;
1840         }
1841         QVERIFY(count == 0);
1842     }
1843
1844     delete anotherChild;
1845     delete namelessChild;
1846     delete child;
1847     */
1848 }
1849
1850 void tst_QWebFrame::overloadedSlots()
1851 {
1852     // should pick myOverloadedSlot(double)
1853     m_myObject->resetQtFunctionInvoked();
1854     evalJS("myObject.myOverloadedSlot(10)");
1855     QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1856
1857     // should pick myOverloadedSlot(double)
1858     m_myObject->resetQtFunctionInvoked();
1859     evalJS("myObject.myOverloadedSlot(10.0)");
1860     QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
1861
1862     // should pick myOverloadedSlot(QString)
1863     m_myObject->resetQtFunctionInvoked();
1864     evalJS("myObject.myOverloadedSlot('10')");
1865     QCOMPARE(m_myObject->qtFunctionInvoked(), 29);
1866
1867     // should pick myOverloadedSlot(bool)
1868     m_myObject->resetQtFunctionInvoked();
1869     evalJS("myObject.myOverloadedSlot(true)");
1870     QCOMPARE(m_myObject->qtFunctionInvoked(), 25);
1871
1872     // should pick myOverloadedSlot(QDateTime)
1873     m_myObject->resetQtFunctionInvoked();
1874     evalJS("myObject.myOverloadedSlot(new Date())");
1875     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
1876
1877     // should pick myOverloadedSlot(QRegExp)
1878     m_myObject->resetQtFunctionInvoked();
1879     evalJS("myObject.myOverloadedSlot(new RegExp())");
1880     QCOMPARE(m_myObject->qtFunctionInvoked(), 34);
1881
1882     // should pick myOverloadedSlot(QVariant)
1883     /* XFAIL
1884     m_myObject->resetQtFunctionInvoked();
1885     QString f = evalJS("myObject.myOverloadedSlot");
1886     f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao")));
1887     QCOMPARE(m_myObject->qtFunctionInvoked(), 35);
1888     */
1889     // should pick myOverloadedSlot(QObject*)
1890     m_myObject->resetQtFunctionInvoked();
1891     evalJS("myObject.myOverloadedSlot(myObject)");
1892     QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1893
1894     // should pick myOverloadedSlot(QObject*)
1895     m_myObject->resetQtFunctionInvoked();
1896     evalJS("myObject.myOverloadedSlot(null)");
1897     QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
1898
1899     // should pick myOverloadedSlot(QStringList)
1900     m_myObject->resetQtFunctionInvoked();
1901     evalJS("myObject.myOverloadedSlot(['hello'])");
1902     QCOMPARE(m_myObject->qtFunctionInvoked(), 42);
1903 }
1904
1905 void tst_QWebFrame::enumerate_data()
1906 {
1907     QTest::addColumn<QStringList>("expectedNames");
1908
1909     QTest::newRow("enumerate all")
1910     << (QStringList()
1911         // meta-object-defined properties:
1912         //   inherited
1913         << "objectName"
1914         //   non-inherited
1915         << "p1" << "p2" << "p4" << "p6"
1916         // dynamic properties
1917         << "dp1" << "dp2" << "dp3"
1918         // inherited slots
1919         << "destroyed(QObject*)" << "destroyed()"
1920         << "deleteLater()"
1921         // not included because it's private:
1922         // << "_q_reregisterTimers(void*)"
1923         // signals
1924         << "mySignal()"
1925         // slots
1926         << "mySlot()" << "myOtherSlot()");
1927 }
1928
1929 void tst_QWebFrame::enumerate()
1930 {
1931     QFETCH(QStringList, expectedNames);
1932
1933     MyEnumTestQObject enumQObject;
1934     // give it some dynamic properties
1935     enumQObject.setProperty("dp1", "dp1");
1936     enumQObject.setProperty("dp2", "dp2");
1937     enumQObject.setProperty("dp3", "dp3");
1938     m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject);
1939
1940     // enumerate in script
1941     {
1942         evalJS("var enumeratedProperties = []");
1943         evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }");
1944         QStringList result = evalJSV("enumeratedProperties").toStringList();
1945         QCOMPARE(result.size(), expectedNames.size());
1946         for (int i = 0; i < expectedNames.size(); ++i)
1947             QCOMPARE(result.at(i), expectedNames.at(i));
1948     }
1949 }
1950
1951 void tst_QWebFrame::objectDeleted()
1952 {
1953     MyQObject* qobj = new MyQObject();
1954     m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj);
1955     evalJS("bar.objectName = 'foo';");
1956     QCOMPARE(qobj->objectName(), QLatin1String("foo"));
1957     evalJS("bar.intProperty = 123;");
1958     QCOMPARE(qobj->intProperty(), 123);
1959     qobj->resetQtFunctionInvoked();
1960     evalJS("bar.myInvokable.call(bar);");
1961     QCOMPARE(qobj->qtFunctionInvoked(), 0);
1962
1963     // do this, to ensure that we cache that it implements call
1964     evalJS("bar()");
1965
1966     // now delete the object
1967     delete qobj;
1968
1969     QCOMPARE(evalJS("typeof bar"), sObject);
1970
1971     // any attempt to access properties of the object should result in an exception
1972     {
1973         QString type;
1974         QString ret = evalJS("bar.objectName", type);
1975         QCOMPARE(type, sError);
1976         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
1977     }
1978     {
1979         QString type;
1980         QString ret = evalJS("bar.objectName = 'foo'", type);
1981         QCOMPARE(type, sError);
1982         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
1983     }
1984
1985     // myInvokable is stored in member table (since we've accessed it before deletion)
1986     {
1987         QString type;
1988         evalJS("bar.myInvokable", type);
1989         QCOMPARE(type, sFunction);
1990     }
1991
1992     {
1993         QString type;
1994         QString ret = evalJS("bar.myInvokable.call(bar);", type);
1995         ret = evalJS("bar.myInvokable(bar)", type);
1996         QCOMPARE(type, sError);
1997         QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
1998     }
1999     // myInvokableWithIntArg is not stored in member table (since we've not accessed it)
2000     {
2001         QString type;
2002         QString ret = evalJS("bar.myInvokableWithIntArg", type);
2003         QCOMPARE(type, sError);
2004         QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2005     }
2006
2007     // access from script
2008     evalJS("window.o = bar;");
2009     {
2010         QString type;
2011         QString ret = evalJS("o.objectName", type);
2012         QCOMPARE(type, sError);
2013         QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
2014     }
2015     {
2016         QString type;
2017         QString ret = evalJS("o.myInvokable()", type);
2018         QCOMPARE(type, sError);
2019         QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject"));
2020     }
2021     {
2022         QString type;
2023         QString ret = evalJS("o.myInvokableWithIntArg(10)", type);
2024         QCOMPARE(type, sError);
2025         QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
2026     }
2027 }
2028
2029 void tst_QWebFrame::typeConversion()
2030 {
2031     m_myObject->resetQtFunctionInvoked();
2032
2033     QDateTime localdt(QDate(2008,1,18), QTime(12,31,0));
2034     QDateTime utclocaldt = localdt.toUTC();
2035     QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC);
2036
2037     // Dates in JS (default to local)
2038     evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))");
2039     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2040     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2041     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt);
2042
2043     m_myObject->resetQtFunctionInvoked();
2044     evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))");
2045     QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
2046     QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
2047     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt);
2048
2049     // Pushing QDateTimes into JS
2050     // Local
2051     evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}");
2052     evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2053     m_myObject->emitMySignalWithDateTimeArg(localdt);
2054     QCOMPARE(evalJS("window.__date_equals"), sTrue);
2055     evalJS("delete window.__date_equals");
2056     m_myObject->emitMySignalWithDateTimeArg(utclocaldt);
2057     QCOMPARE(evalJS("window.__date_equals"), sTrue);
2058     evalJS("delete window.__date_equals");
2059     evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2060
2061     // UTC
2062     evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }");
2063     evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)");
2064     m_myObject->emitMySignalWithDateTimeArg(utcdt);
2065     QCOMPARE(evalJS("window.__date_equals"), sTrue);
2066     evalJS("delete window.__date_equals");
2067     evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;");
2068
2069     // ### RegExps
2070 }
2071
2072 class StringListTestObject : public QObject {
2073     Q_OBJECT
2074 public Q_SLOTS:
2075     QVariant stringList()
2076     {
2077         return QStringList() << "Q" << "t";
2078     };
2079 };
2080
2081 void tst_QWebFrame::arrayObjectEnumerable()
2082 {
2083     QWebPage page;
2084     QWebFrame* frame = page.mainFrame();
2085     QObject* qobject = new StringListTestObject();
2086     frame->addToJavaScriptWindowObject("test", qobject, QScriptEngine::ScriptOwnership);
2087
2088     const QString script("var stringArray = test.stringList();"
2089                          "var result = '';"
2090                          "for (var i in stringArray) {"
2091                          "    result += stringArray[i];"
2092                          "}"
2093                          "result;");
2094     QCOMPARE(frame->evaluateJavaScript(script).toString(), QString::fromLatin1("Qt"));
2095 }
2096
2097 void tst_QWebFrame::symmetricUrl()
2098 {
2099     QVERIFY(m_view->url().isEmpty());
2100
2101     QCOMPARE(m_view->history()->count(), 0);
2102
2103     QUrl dataUrl("data:text/html,<h1>Test");
2104
2105     m_view->setUrl(dataUrl);
2106     QCOMPARE(m_view->url(), dataUrl);
2107     QCOMPARE(m_view->history()->count(), 0);
2108
2109     // loading is _not_ immediate, so the text isn't set just yet.
2110     QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty());
2111
2112     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2113
2114     QCOMPARE(m_view->history()->count(), 1);
2115     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test"));
2116
2117     QUrl dataUrl2("data:text/html,<h1>Test2");
2118     QUrl dataUrl3("data:text/html,<h1>Test3");
2119
2120     m_view->setUrl(dataUrl2);
2121     m_view->setUrl(dataUrl3);
2122
2123     QCOMPARE(m_view->url(), dataUrl3);
2124
2125     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2126
2127     QCOMPARE(m_view->history()->count(), 2);
2128
2129     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3"));
2130 }
2131
2132 void tst_QWebFrame::progressSignal()
2133 {
2134     QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int)));
2135
2136     QUrl dataUrl("data:text/html,<h1>Test");
2137     m_view->setUrl(dataUrl);
2138
2139     ::waitForSignal(m_view, SIGNAL(loadFinished(bool)));
2140
2141     QVERIFY(progressSpy.size() >= 2);
2142
2143     // WebKit defines initialProgressValue as 10%, not 0%
2144     QCOMPARE(progressSpy.first().first().toInt(), 10);
2145
2146     // But we always end at 100%
2147     QCOMPARE(progressSpy.last().first().toInt(), 100);
2148 }
2149
2150 void tst_QWebFrame::urlChange()
2151 {
2152     QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2153
2154     QUrl dataUrl("data:text/html,<h1>Test");
2155     m_view->setUrl(dataUrl);
2156
2157     ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2158
2159     QCOMPARE(urlSpy.size(), 1);
2160
2161     QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>");
2162     m_view->setUrl(dataUrl2);
2163
2164     ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl)));
2165
2166     QCOMPARE(urlSpy.size(), 2);
2167 }
2168
2169
2170 void tst_QWebFrame::domCycles()
2171 {
2172     m_view->setHtml("<html><body>");
2173     QVariant v = m_page->mainFrame()->evaluateJavaScript("document");
2174     QVERIFY(v.type() == QVariant::Map);
2175 }
2176
2177 class FakeReply : public QNetworkReply {
2178     Q_OBJECT
2179
2180 public:
2181     FakeReply(const QNetworkRequest& request, QObject* parent = 0)
2182         : QNetworkReply(parent)
2183     {
2184         setOperation(QNetworkAccessManager::GetOperation);
2185         setRequest(request);
2186         if (request.url() == QUrl("qrc:/test1.html")) {
2187             setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html"));
2188             setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html"));
2189         }
2190 #ifndef QT_NO_OPENSSL
2191         else if (request.url() == QUrl("qrc:/fake-ssl-error.html"))
2192             setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error
2193 #endif
2194         else if (request.url() == QUrl("http://abcdef.abcdef/"))
2195             setError(QNetworkReply::HostNotFoundError, tr("Invalid URL"));
2196
2197         open(QIODevice::ReadOnly);
2198         QTimer::singleShot(0, this, SLOT(timeout()));
2199     }
2200     ~FakeReply()
2201     {
2202         close();
2203     }
2204     virtual void abort() {}
2205     virtual void close() {}
2206
2207 protected:
2208     qint64 readData(char*, qint64)
2209     {
2210         return 0;
2211     }
2212
2213 private slots:
2214     void timeout()
2215     {
2216         if (request().url() == QUrl("qrc://test1.html"))
2217             emit error(this->error());
2218         else if (request().url() == QUrl("http://abcdef.abcdef/"))
2219             emit metaDataChanged();
2220 #ifndef QT_NO_OPENSSL
2221         else if (request().url() == QUrl("qrc:/fake-ssl-error.html"))
2222             return;
2223 #endif
2224
2225         emit readyRead();
2226         emit finished();
2227     }
2228 };
2229
2230 class FakeNetworkManager : public QNetworkAccessManager {
2231     Q_OBJECT
2232
2233 public:
2234     FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { }
2235
2236 protected:
2237     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
2238     {
2239         QString url = request.url().toString();
2240         if (op == QNetworkAccessManager::GetOperation) {
2241             if (url == "qrc:/test1.html" ||  url == "http://abcdef.abcdef/")
2242                 return new FakeReply(request, this);
2243 #ifndef QT_NO_OPENSSL
2244             else if (url == "qrc:/fake-ssl-error.html") {
2245                 FakeReply* reply = new FakeReply(request, this);
2246                 QList<QSslError> errors;
2247                 emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError));
2248                 return reply;
2249             }
2250 #endif
2251        }
2252
2253         return QNetworkAccessManager::createRequest(op, request, outgoingData);
2254     }
2255 };
2256
2257 void tst_QWebFrame::requestedUrl()
2258 {
2259     QWebPage page;
2260     QWebFrame* frame = page.mainFrame();
2261
2262     // in few seconds, the image should be completely loaded
2263     QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2264     FakeNetworkManager* networkManager = new FakeNetworkManager(&page);
2265     page.setNetworkAccessManager(networkManager);
2266
2267     frame->setUrl(QUrl("qrc:/test1.html"));
2268     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2269     QCOMPARE(spy.count(), 1);
2270     QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html"));
2271     QCOMPARE(frame->url(), QUrl("qrc:/test2.html"));
2272
2273     frame->setUrl(QUrl("qrc:/non-existent.html"));
2274     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2275     QCOMPARE(spy.count(), 2);
2276     QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html"));
2277     QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html"));
2278
2279     frame->setUrl(QUrl("http://abcdef.abcdef"));
2280     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2281     QCOMPARE(spy.count(), 3);
2282     QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/"));
2283     QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/"));
2284
2285 #ifndef QT_NO_OPENSSL
2286     qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
2287     qRegisterMetaType<QNetworkReply* >("QNetworkReply*");
2288
2289     QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
2290     frame->setUrl(QUrl("qrc:/fake-ssl-error.html"));
2291     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2292     QCOMPARE(spy2.count(), 1);
2293     QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html"));
2294     QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html"));
2295 #endif
2296 }
2297
2298 void tst_QWebFrame::javaScriptWindowObjectCleared_data()
2299 {
2300     QTest::addColumn<QString>("html");
2301     QTest::addColumn<int>("signalCount");
2302     QTest::newRow("with <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 1;
2303     QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0;
2304 }
2305
2306 void tst_QWebFrame::javaScriptWindowObjectCleared()
2307 {
2308     QWebPage page;
2309     QWebFrame* frame = page.mainFrame();
2310     QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2311     QFETCH(QString, html);
2312     frame->setHtml(html);
2313
2314     QFETCH(int, signalCount);
2315     QCOMPARE(spy.count(), signalCount);
2316 }
2317
2318 void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate()
2319 {
2320     QWebPage page;
2321     QWebFrame* frame = page.mainFrame();
2322     QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared()));
2323     frame->setHtml("<html></html>");
2324     QCOMPARE(spy.count(), 0);
2325     frame->evaluateJavaScript("var a = 'a';");
2326     QCOMPARE(spy.count(), 1);
2327     // no new clear for a new script:
2328     frame->evaluateJavaScript("var a = 1;");
2329     QCOMPARE(spy.count(), 1);
2330 }
2331
2332 void tst_QWebFrame::setHtml()
2333 {
2334     QString html("<html><head></head><body><p>hello world</p></body></html>");
2335     m_view->page()->mainFrame()->setHtml(html);
2336     QCOMPARE(m_view->page()->mainFrame()->toHtml(), html);
2337 }
2338
2339 void tst_QWebFrame::setHtmlWithResource()
2340 {
2341     QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>");
2342
2343     QWebPage page;
2344     QWebFrame* frame = page.mainFrame();
2345
2346     // in few seconds, the image should be completey loaded
2347     QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2348     frame->setHtml(html);
2349     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2350     QCOMPARE(spy.count(), 1);
2351
2352     QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2353     QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2354     QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2355
2356     QString html2 =
2357         "<html>"
2358             "<head>"
2359                 "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />"
2360             "</head>"
2361             "<body>"
2362                 "<p id='idP'>some text</p>"
2363             "</body>"
2364         "</html>";
2365
2366     // in few seconds, the CSS should be completey loaded
2367     frame->setHtml(html2);
2368     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2369     QCOMPARE(spy.size(), 2);
2370
2371     QWebElement p = frame->documentElement().findAll("p").at(0);
2372     QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red"));
2373 }
2374
2375 void tst_QWebFrame::setHtmlWithBaseURL()
2376 {
2377     if (!QDir(TESTS_SOURCE_DIR).exists())
2378         QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
2379
2380     QDir::setCurrent(TESTS_SOURCE_DIR);
2381
2382     QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>");
2383
2384     QWebPage page;
2385     QWebFrame* frame = page.mainFrame();
2386
2387     // in few seconds, the image should be completey loaded
2388     QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
2389
2390     frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
2391     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2392     QCOMPARE(spy.count(), 1);
2393
2394     QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1);
2395     QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128);
2396     QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128);
2397
2398     // no history item has to be added.
2399     QCOMPARE(m_view->page()->history()->count(), 0);
2400 }
2401
2402 class TestNetworkManager : public QNetworkAccessManager
2403 {
2404 public:
2405     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
2406
2407     QList<QUrl> requestedUrls;
2408
2409 protected:
2410     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
2411         requestedUrls.append(request.url());
2412         QNetworkRequest redirectedRequest = request;
2413         redirectedRequest.setUrl(QUrl("data:text/html,<p>hello"));
2414         return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData);
2415     }
2416 };
2417
2418 void tst_QWebFrame::ipv6HostEncoding()
2419 {
2420     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2421     m_page->setNetworkAccessManager(networkManager);
2422     networkManager->requestedUrls.clear();
2423
2424     QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html");
2425     m_view->setHtml("<p>Hi", baseUrl);
2426     m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();"
2427             "r.open('GET', 'http://[::1]/test.xml', false);"
2428             "r.send(null);"
2429             );
2430     QCOMPARE(networkManager->requestedUrls.count(), 1);
2431     QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml"));
2432 }
2433
2434 void tst_QWebFrame::metaData()
2435 {
2436     m_view->setHtml("<html>"
2437                     "    <head>"
2438                     "        <meta name=\"description\" content=\"Test description\">"
2439                     "        <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">"
2440                     "    </head>"
2441                     "</html>");
2442
2443     QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData();
2444
2445     QCOMPARE(metaData.count(), 2);
2446
2447     QCOMPARE(metaData.value("description"), QString("Test description"));
2448     QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css"));
2449     QCOMPARE(metaData.value("nonexistant"), QString());
2450
2451     m_view->setHtml("<html>"
2452                     "    <head>"
2453                     "        <meta name=\"samekey\" content=\"FirstValue\">"
2454                     "        <meta name=\"samekey\" content=\"SecondValue\">"
2455                     "    </head>"
2456                     "</html>");
2457
2458     metaData = m_view->page()->mainFrame()->metaData();
2459
2460     QCOMPARE(metaData.count(), 2);
2461
2462     QStringList values = metaData.values("samekey");
2463     QCOMPARE(values.count(), 2);
2464
2465     QVERIFY(values.contains("FirstValue"));
2466     QVERIFY(values.contains("SecondValue"));
2467
2468     QCOMPARE(metaData.value("nonexistant"), QString());
2469 }
2470
2471 void tst_QWebFrame::popupFocus()
2472 {
2473     QWebView view;
2474     view.setHtml("<html>"
2475                  "    <body>"
2476                  "        <select name=\"select\">"
2477                  "            <option>1</option>"
2478                  "            <option>2</option>"
2479                  "        </select>"
2480                  "        <input type=\"text\"> </input>"
2481                  "        <textarea name=\"text_area\" rows=\"3\" cols=\"40\">"
2482                  "This test checks whether showing and hiding a popup"
2483                  "takes the focus away from the webpage."
2484                  "        </textarea>"
2485                  "    </body>"
2486                  "</html>");
2487     view.resize(400, 100);
2488     view.show();
2489     view.setFocus();
2490     QTRY_VERIFY(view.hasFocus());
2491
2492     // open the popup by clicking. check if focus is on the popup
2493     QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(25, 25));
2494     QObject* webpopup = firstChildByClassName(&view, "WebCore::QWebPopup");
2495     QComboBox* combo = qobject_cast<QComboBox*>(webpopup);
2496     QVERIFY(combo != 0);
2497     QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup
2498
2499     // hide the popup and check if focus is on the page
2500     combo->hidePopup();
2501     QTRY_VERIFY(view.hasFocus() && !combo->view()->hasFocus()); // Focus should be back on the WebView
2502
2503     // double the flashing time, should at least blink once already
2504     int delay = qApp->cursorFlashTime() * 2;
2505
2506     // focus the lineedit and check if it blinks
2507     QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(200, 25));
2508     m_popupTestView = &view;
2509     view.installEventFilter( this );
2510     QTest::qWait(delay);
2511     QVERIFY2(m_popupTestPaintCount >= 3,
2512              "The input field should have a blinking caret");
2513 }
2514
2515 void tst_QWebFrame::hitTestContent()
2516 {
2517     QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\">link text</a></body></html>");
2518
2519     QWebPage page;
2520     QWebFrame* frame = page.mainFrame();
2521     frame->setHtml(html);
2522     page.setViewportSize(QSize(200, 0)); //no height so link is not visible
2523     QWebHitTestResult result = frame->hitTestContent(QPoint(10, 100));
2524     QCOMPARE(result.linkText(), QString("link text"));
2525     QWebElement link = result.linkElement();
2526     QCOMPARE(link.attribute("target"), QString("_foo"));
2527 }
2528
2529 void tst_QWebFrame::jsByteArray()
2530 {
2531     QByteArray ba("hello world");
2532     m_myObject->setByteArrayProperty(ba);
2533
2534     // read-only property
2535     QCOMPARE(m_myObject->byteArrayProperty(), ba);
2536     QString type;
2537     QVariant v = evalJSV("myObject.byteArrayProperty");
2538     QCOMPARE(int(v.type()), int(QVariant::ByteArray));
2539
2540     QCOMPARE(v.toByteArray(), ba);
2541 }
2542
2543 void tst_QWebFrame::ownership()
2544 {
2545     // test ownership
2546     {
2547         QPointer<QObject> ptr = new QObject();
2548         QVERIFY(ptr != 0);
2549         {
2550             QWebPage page;
2551             QWebFrame* frame = page.mainFrame();
2552             frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership);
2553         }
2554         QVERIFY(ptr == 0);
2555     }
2556     {
2557         QPointer<QObject> ptr = new QObject();
2558         QVERIFY(ptr != 0);
2559         QObject* before = ptr;
2560         {
2561             QWebPage page;
2562             QWebFrame* frame = page.mainFrame();
2563             frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership);
2564         }
2565         QVERIFY(ptr == before);
2566         delete ptr;
2567     }
2568     {
2569         QObject* parent = new QObject();
2570         QObject* child = new QObject(parent);
2571         QWebPage page;
2572         QWebFrame* frame = page.mainFrame();
2573         frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership);
2574         QVariant v = frame->evaluateJavaScript("test");
2575         QCOMPARE(qvariant_cast<QObject*>(v), child);
2576         delete parent;
2577         v = frame->evaluateJavaScript("test");
2578         QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0);
2579     }
2580     {
2581         QPointer<QObject> ptr = new QObject();
2582         QVERIFY(ptr != 0);
2583         {
2584             QWebPage page;
2585             QWebFrame* frame = page.mainFrame();
2586             frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership);
2587         }
2588         // no parent, so it should be like ScriptOwnership
2589         QVERIFY(ptr == 0);
2590     }
2591     {
2592         QObject* parent = new QObject();
2593         QPointer<QObject> child = new QObject(parent);
2594         QVERIFY(child != 0);
2595         {
2596             QWebPage page;
2597             QWebFrame* frame = page.mainFrame();
2598             frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership);
2599         }
2600         // has parent, so it should be like QtOwnership
2601         QVERIFY(child != 0);
2602         delete parent;
2603     }
2604 }
2605
2606 void tst_QWebFrame::nullValue()
2607 {
2608     QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null");
2609     QVERIFY(v.isNull());
2610 }
2611
2612 void tst_QWebFrame::baseUrl_data()
2613 {
2614     QTest::addColumn<QString>("html");
2615     QTest::addColumn<QUrl>("loadUrl");
2616     QTest::addColumn<QUrl>("url");
2617     QTest::addColumn<QUrl>("baseUrl");
2618
2619     QTest::newRow("null") << QString() << QUrl()
2620                           << QUrl("about:blank") << QUrl("about:blank");
2621
2622     QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/")
2623                          << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/");
2624
2625     QString html = "<html>"
2626         "<head>"
2627             "<base href=\"http://foobaz.bar/\" />"
2628         "</head>"
2629     "</html>";
2630     QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/")
2631                                    << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/");
2632 }
2633
2634 void tst_QWebFrame::baseUrl()
2635 {
2636     QFETCH(QString, html);
2637     QFETCH(QUrl, loadUrl);
2638     QFETCH(QUrl, url);
2639     QFETCH(QUrl, baseUrl);
2640
2641     m_page->mainFrame()->setHtml(html, loadUrl);
2642     QCOMPARE(m_page->mainFrame()->url(), url);
2643     QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl);
2644 }
2645
2646 void tst_QWebFrame::hasSetFocus()
2647 {
2648     QString html("<html><body><p>top</p>" \
2649                     "<iframe width='80%' height='30%'/>" \
2650                  "</body></html>");
2651
2652     QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
2653     m_page->mainFrame()->setHtml(html);
2654
2655     waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200);
2656     QCOMPARE(loadSpy.size(), 1);
2657
2658     QList<QWebFrame*> children = m_page->mainFrame()->childFrames();
2659     QWebFrame* frame = children.at(0);
2660     QString innerHtml("<html><body><p>another iframe</p>" \
2661                         "<iframe width='80%' height='30%'/>" \
2662                       "</body></html>");
2663     frame->setHtml(innerHtml);
2664
2665     waitForSignal(frame, SIGNAL(loadFinished(bool)), 200);
2666     QCOMPARE(loadSpy.size(), 2);
2667
2668     m_page->mainFrame()->setFocus();
2669     QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2670
2671     for (int i = 0; i < children.size(); ++i) {
2672         children.at(i)->setFocus();
2673         QTRY_VERIFY(children.at(i)->hasFocus());
2674         QVERIFY(!m_page->mainFrame()->hasFocus());
2675     }
2676
2677     m_page->mainFrame()->setFocus();
2678     QTRY_VERIFY(m_page->mainFrame()->hasFocus());
2679 }
2680
2681 void tst_QWebFrame::render()
2682 {
2683     QString html("<html>" \
2684                     "<head><style>" \
2685                        "body, iframe { margin: 0px; border: none; }" \
2686                     "</style></head>" \
2687                     "<body><iframe width='100px' height='100px'/></body>" \
2688                  "</html>");
2689
2690     QWebPage page;
2691     page.mainFrame()->setHtml(html);
2692
2693     QList<QWebFrame*> frames = page.mainFrame()->childFrames();
2694     QWebFrame *frame = frames.at(0);
2695     QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>");
2696     frame->setHtml(innerHtml);
2697
2698     QPicture picture;
2699
2700     QSize size = page.mainFrame()->contentsSize();
2701     page.setViewportSize(size);
2702
2703     // render contents layer only (the iframe is smaller than the image, so it will have scrollbars)
2704     QPainter painter1(&picture);
2705     frame->render(&painter1, QWebFrame::ContentsLayer);
2706     painter1.end();
2707
2708     QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width());
2709     QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height());
2710
2711     // render everything, should be the size of the iframe
2712     QPainter painter2(&picture);
2713     frame->render(&painter2, QWebFrame::AllLayers);
2714     painter2.end();
2715
2716     QCOMPARE(size.width(), picture.boundingRect().width());   // width: 100px
2717     QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px
2718 }
2719
2720 void tst_QWebFrame::scrollPosition()
2721 {
2722     // enlarged image in a small viewport, to provoke the scrollbars to appear
2723     QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>");
2724
2725     QWebPage page;
2726     page.setViewportSize(QSize(200, 200));
2727
2728     QWebFrame* frame = page.mainFrame();
2729     frame->setHtml(html);
2730     frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
2731     frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
2732
2733     // try to set the scroll offset programmatically
2734     frame->setScrollPosition(QPoint(23, 29));
2735     QCOMPARE(frame->scrollPosition().x(), 23);
2736     QCOMPARE(frame->scrollPosition().y(), 29);
2737
2738     int x = frame->evaluateJavaScript("window.scrollX").toInt();
2739     int y = frame->evaluateJavaScript("window.scrollY").toInt();
2740     QCOMPARE(x, 23);
2741     QCOMPARE(y, 29);
2742 }
2743
2744 void tst_QWebFrame::evaluateWillCauseRepaint()
2745 {
2746     QWebView view;
2747     QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">"
2748                     "junk</div>bottom</body></html>");
2749     view.setHtml(html);
2750     view.show();
2751
2752 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
2753     QTest::qWaitForWindowShown(&view);
2754 #else
2755     QTest::qWait(2000); 
2756 #endif
2757
2758     view.page()->mainFrame()->evaluateJavaScript(
2759         "document.getElementById('junk').style.display = 'none';");
2760
2761     ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect)));
2762 }
2763
2764 class TestFactory : public QObject
2765 {
2766     Q_OBJECT
2767 public:
2768     TestFactory()
2769         : obj(0), counter(0)
2770     {}
2771
2772     Q_INVOKABLE QObject* getNewObject()
2773     {
2774         delete obj;
2775         obj = new QObject(this);
2776         obj->setObjectName(QLatin1String("test") + QString::number(++counter));
2777         return obj;
2778
2779     }
2780
2781     QObject* obj;
2782     int counter;
2783 };
2784
2785 void tst_QWebFrame::qObjectWrapperWithSameIdentity()
2786 {
2787     m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>"
2788                     "<body><span id='span1'>test</span></body>");
2789
2790     QWebFrame* mainFrame = m_view->page()->mainFrame();
2791     QCOMPARE(mainFrame->toPlainText(), QString("test"));
2792
2793     mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership);
2794
2795     mainFrame->evaluateJavaScript("triggerBug();");
2796     QCOMPARE(mainFrame->toPlainText(), QString("test1"));
2797
2798     mainFrame->evaluateJavaScript("triggerBug();");
2799     QCOMPARE(mainFrame->toPlainText(), QString("test2"));
2800 }
2801
2802 void tst_QWebFrame::scrollRecursively()
2803 {
2804     // The test content is 
2805     // a nested frame set
2806     // The main frame scrolls
2807     // and has two children
2808     // an iframe and a div overflow
2809     // both scroll
2810     QWebView webView;
2811     QWebPage* webPage = webView.page();
2812     QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
2813     QUrl url = QUrl("qrc:///testiframe.html");
2814     webPage->mainFrame()->load(url);
2815     QTRY_COMPARE(loadSpy.count(), 1);
2816
2817     QList<QWebFrame*> children =  webPage->mainFrame()->childFrames();
2818     QVERIFY(children.count() == 1);
2819
2820     // 1st test
2821     // call scrollRecursively over mainframe
2822     // verify scrolled
2823     // verify scroll postion changed
2824     QPoint scrollPosition(webPage->mainFrame()->scrollPosition());
2825     QVERIFY(webPage->mainFrame()->scrollRecursively(10, 10));
2826     QVERIFY(scrollPosition != webPage->mainFrame()->scrollPosition());
2827
2828     // 2nd test
2829     // call scrollRecursively over child iframe
2830     // verify scrolled
2831     // verify child scroll position changed
2832     // verify parent's scroll position did not change
2833     scrollPosition = webPage->mainFrame()->scrollPosition();
2834     QPoint childScrollPosition = children.at(0)->scrollPosition();
2835     QVERIFY(children.at(0)->scrollRecursively(10, 10));
2836     QVERIFY(scrollPosition == webPage->mainFrame()->scrollPosition());
2837     QVERIFY(childScrollPosition != children.at(0)->scrollPosition());
2838
2839     // 3rd test
2840     // call scrollRecursively over div overflow
2841     // verify scrolled == true
2842     // verify parent and child frame's scroll postion did not change
2843     QWebElement div = webPage->mainFrame()->documentElement().findFirst("#content1");
2844     QMouseEvent evpres(QEvent::MouseMove, div.geometry().center(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
2845     webPage->event(&evpres);
2846     scrollPosition = webPage->mainFrame()->scrollPosition();
2847     childScrollPosition = children.at(0)->scrollPosition();
2848     QVERIFY(webPage->mainFrame()->scrollRecursively(5, 5));
2849     QVERIFY(childScrollPosition == children.at(0)->scrollPosition());
2850     QVERIFY(scrollPosition == webPage->mainFrame()->scrollPosition());
2851
2852     // 4th test
2853     // call scrollRecursively twice over childs iframe
2854     // verify scrolled == true first time
2855     // verify parent's scroll == true second time
2856     // verify parent and childs scroll position changed
2857     childScrollPosition = children.at(0)->scrollPosition();
2858     QVERIFY(children.at(0)->scrollRecursively(-10, -10));
2859     QVERIFY(childScrollPosition != children.at(0)->scrollPosition());
2860     scrollPosition = webPage->mainFrame()->scrollPosition();
2861     QVERIFY(children.at(0)->scrollRecursively(-10, -10));
2862     QVERIFY(scrollPosition != webPage->mainFrame()->scrollPosition());
2863
2864 }
2865
2866 void tst_QWebFrame::introspectQtMethods_data()
2867 {
2868     QTest::addColumn<QString>("objectExpression");
2869     QTest::addColumn<QString>("methodName");
2870     QTest::addColumn<QStringList>("expectedPropertyNames");
2871
2872     QTest::newRow("myObject.mySignal")
2873         << "myObject" << "mySignal" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2874     QTest::newRow("myObject.mySlot")
2875         << "myObject" << "mySlot" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2876     QTest::newRow("myObject.myInvokable")
2877         << "myObject" << "myInvokable" << (QStringList() << "connect" << "disconnect" << "length" << "name");
2878     QTest::newRow("myObject.mySignal.connect")
2879         << "myObject.mySignal" << "connect" << (QStringList() << "length" << "name");
2880     QTest::newRow("myObject.mySignal.disconnect")
2881         << "myObject.mySignal" << "disconnect" << (QStringList() << "length" << "name");
2882 }
2883
2884 void tst_QWebFrame::introspectQtMethods()
2885 {
2886     QFETCH(QString, objectExpression);
2887     QFETCH(QString, methodName);
2888     QFETCH(QStringList, expectedPropertyNames);
2889
2890     QString methodLookup = QString::fromLatin1("%0['%1']").arg(objectExpression).arg(methodName);
2891     QCOMPARE(evalJSV(QString::fromLatin1("Object.getOwnPropertyNames(%0).sort()").arg(methodLookup)).toStringList(), expectedPropertyNames);
2892
2893     for (int i = 0; i < expectedPropertyNames.size(); ++i) {
2894         QString name = expectedPropertyNames.at(i);
2895         QCOMPARE(evalJS(QString::fromLatin1("%0.hasOwnProperty('%1')").arg(methodLookup).arg(name)), sTrue);
2896         evalJS(QString::fromLatin1("var descriptor = Object.getOwnPropertyDescriptor(%0, '%1')").arg(methodLookup).arg(name));
2897         QCOMPARE(evalJS("typeof descriptor"), QString::fromLatin1("object"));
2898         QCOMPARE(evalJS("descriptor.get"), sUndefined);
2899         QCOMPARE(evalJS("descriptor.set"), sUndefined);
2900         QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue);
2901         QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse);
2902         QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse);
2903     }
2904
2905     QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty());
2906 }
2907
2908 QTEST_MAIN(tst_QWebFrame)
2909 #include "tst_qwebframe.moc"