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