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