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