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