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