OSDN Git Service

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