OSDN Git Service

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