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