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