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