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