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