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