OSDN Git Service

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