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