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