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