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