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