OSDN Git Service

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