qt_runtime.cpp 70.3 KB
Newer Older
andersca@apple.com's avatar
andersca@apple.com committed
1
/*
2
 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
andersca@apple.com's avatar
andersca@apple.com committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "config.h"
#include "qt_runtime.h"
22

mrowe@apple.com's avatar
mrowe@apple.com committed
23
#include "BooleanObject.h"
24 25 26
#include "DateInstance.h"
#include "DateMath.h"
#include "DatePrototype.h"
27
#include "DumpRenderTreeSupportQt.h"
28
#include "FunctionPrototype.h"
hausmann@webkit.org's avatar
hausmann@webkit.org committed
29
#include "Interpreter.h"
30
#include "JSArray.h"
hausmann@webkit.org's avatar
hausmann@webkit.org committed
31
#include "JSByteArray.h"
32
#include "JSDocument.h"
33
#include "JSDOMBinding.h"
34 35
#include "JSDOMWindow.h"
#include <JSFunction.h>
36
#include "JSGlobalObject.h"
37
#include "JSHTMLElement.h"
ap@webkit.org's avatar
ap@webkit.org committed
38
#include "JSLock.h"
darin@apple.com's avatar
darin@apple.com committed
39
#include "JSObject.h"
40
#include "ObjectPrototype.h"
41
#include "PropertyNameArray.h"
42
#include "RegExpConstructor.h"
darin@apple.com's avatar
darin@apple.com committed
43
#include "RegExpObject.h"
44 45
#include "qdatetime.h"
#include "qdebug.h"
andersca@apple.com's avatar
andersca@apple.com committed
46
#include "qmetaobject.h"
47
#include "qmetatype.h"
andersca@apple.com's avatar
andersca@apple.com committed
48 49
#include "qobject.h"
#include "qstringlist.h"
50
#include "qt_instance.h"
51
#include "qt_pixmapruntime.h"
andersca@apple.com's avatar
andersca@apple.com committed
52
#include "qvarlengtharray.h"
53
#include "qwebelement.h"
andersca@apple.com's avatar
andersca@apple.com committed
54
#include <limits.h>
mrowe@apple.com's avatar
mrowe@apple.com committed
55
#include <runtime/Error.h>
56 57
#include <runtime_array.h>
#include <runtime_object.h>
andersca@apple.com's avatar
andersca@apple.com committed
58 59 60 61 62 63

// QtScript has these
Q_DECLARE_METATYPE(QObjectList);
Q_DECLARE_METATYPE(QList<int>);
Q_DECLARE_METATYPE(QVariant);

64
using namespace WebCore;
andersca@apple.com's avatar
andersca@apple.com committed
65

66
namespace JSC {
andersca@apple.com's avatar
andersca@apple.com committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
namespace Bindings {

// Debugging
//#define QTWK_RUNTIME_CONVERSION_DEBUG
//#define QTWK_RUNTIME_MATCH_DEBUG

class QWKNoDebug
{
public:
    inline QWKNoDebug(){}
    inline ~QWKNoDebug(){}

    template<typename T>
    inline QWKNoDebug &operator<<(const T &) { return *this; }
};

#ifdef QTWK_RUNTIME_CONVERSION_DEBUG
#define qConvDebug() qDebug()
#else
#define qConvDebug() QWKNoDebug()
#endif

#ifdef QTWK_RUNTIME_MATCH_DEBUG
#define qMatchDebug() qDebug()
#else
#define qMatchDebug() QWKNoDebug()
#endif

typedef enum {
96
    Variant = 0,
andersca@apple.com's avatar
andersca@apple.com committed
97 98 99 100 101 102 103 104
    Number,
    Boolean,
    String,
    Date,
    RegExp,
    Array,
    QObj,
    Object,
105
    Null,
hausmann@webkit.org's avatar
hausmann@webkit.org committed
106 107
    RTArray,
    JSByteArray
andersca@apple.com's avatar
andersca@apple.com committed
108 109
} JSRealType;

110 111 112 113 114 115 116 117 118 119 120 121
#if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG)
QDebug operator<<(QDebug dbg, const JSRealType &c)
{
     const char *map[] = { "Variant", "Number", "Boolean", "String", "Date",
         "RegExp", "Array", "RTObject", "Object", "Null", "RTArray"};

     dbg.nospace() << "JSType(" << ((int)c) << ", " <<  map[c] << ")";

     return dbg.space();
}
#endif

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
// this is here as a proxy, so we'd have a class to friend in QWebElement,
// as getting/setting a WebCore in QWebElement is private
class QtWebElementRuntime {
public:
    static QWebElement create(Element* element)
    {
        return QWebElement(element);
    }

    static Element* get(const QWebElement& element)
    {
        return element.m_element;
    }
};

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
// this is here as a proxy, so we'd have a class to friend in QDRTNode,
// as getting/setting a WebCore in QDRTNode is private.
// We only need to pass WebCore Nodes for layout tests.
class QtDRTNodeRuntime {
public:
    static QDRTNode create(Node* node)
    {
        return QDRTNode(node);
    }

    static Node* get(const QDRTNode& node)
    {
        return node.m_node;
    }
};

ggaren@apple.com's avatar
ggaren@apple.com committed
153
static JSRealType valueRealType(ExecState* exec, JSValue val)
andersca@apple.com's avatar
andersca@apple.com committed
154
{
155
    if (val.isNumber())
andersca@apple.com's avatar
andersca@apple.com committed
156
        return Number;
157
    else if (val.isString())
andersca@apple.com's avatar
andersca@apple.com committed
158
        return String;
159
    else if (val.isBoolean())
andersca@apple.com's avatar
andersca@apple.com committed
160
        return Boolean;
161
    else if (val.isNull())
andersca@apple.com's avatar
andersca@apple.com committed
162
        return Null;
163
    else if (isJSByteArray(&exec->globalData(), val))
hausmann@webkit.org's avatar
hausmann@webkit.org committed
164
        return JSByteArray;
165 166
    else if (val.isObject()) {
        JSObject *object = val.toObject(exec);
167 168
        if (object->inherits(&RuntimeArray::s_info))  // RuntimeArray 'inherits' from Array, but not in C++
            return RTArray;
darin@apple.com's avatar
darin@apple.com committed
169
        else if (object->inherits(&JSArray::info))
andersca@apple.com's avatar
andersca@apple.com committed
170 171 172
            return Array;
        else if (object->inherits(&DateInstance::info))
            return Date;
darin@apple.com's avatar
darin@apple.com committed
173
        else if (object->inherits(&RegExpObject::info))
andersca@apple.com's avatar
andersca@apple.com committed
174
            return RegExp;
175
        else if (object->inherits(&RuntimeObject::s_info))
andersca@apple.com's avatar
andersca@apple.com committed
176 177 178 179 180 181 182
            return QObj;
        return Object;
    }

    return String; // I don't know.
}

183
QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance, HashSet<JSObject*>* visitedObjects, int recursionLimit)
andersca@apple.com's avatar
andersca@apple.com committed
184
{
185 186 187
    --recursionLimit;

    if (!value || !recursionLimit)
188 189
        return QVariant();

190
    JSObject* object = 0;
191 192
    if (value.isObject()) {
        object = value.toObject(exec);
193 194 195 196 197 198
        if (visitedObjects->contains(object))
            return QVariant();

        visitedObjects->add(object);
    }

andersca@apple.com's avatar
andersca@apple.com committed
199
    // check magic pointer values before dereferencing value
200
    if (value == jsNaN()
201 202 203
        || (value == jsUndefined()
            && hint != QMetaType::QString
            && hint != (QMetaType::Type) qMetaTypeId<QVariant>())) {
andersca@apple.com's avatar
andersca@apple.com committed
204 205 206 207 208
        if (distance)
            *distance = -1;
        return QVariant();
    }

ap@webkit.org's avatar
ap@webkit.org committed
209
    JSLock lock(SilenceAssertionsOnly);
andersca@apple.com's avatar
andersca@apple.com committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    JSRealType type = valueRealType(exec, value);
    if (hint == QMetaType::Void) {
        switch(type) {
            case Number:
                hint = QMetaType::Double;
                break;
            case Boolean:
                hint = QMetaType::Bool;
                break;
            case String:
            default:
                hint = QMetaType::QString;
                break;
            case Date:
                hint = QMetaType::QDateTime;
                break;
            case RegExp:
                hint = QMetaType::QRegExp;
                break;
229
            case Object:
230 231 232 233 234 235
                if (object->inherits(&NumberObject::info))
                    hint = QMetaType::Double;
                else if (object->inherits(&BooleanObject::info))
                    hint = QMetaType::Bool;
                else
                    hint = QMetaType::QVariantMap;
236
                break;
andersca@apple.com's avatar
andersca@apple.com committed
237 238 239
            case QObj:
                hint = QMetaType::QObjectStar;
                break;
hausmann@webkit.org's avatar
hausmann@webkit.org committed
240 241 242
            case JSByteArray:
                hint = QMetaType::QByteArray;
                break;
andersca@apple.com's avatar
andersca@apple.com committed
243
            case Array:
244
            case RTArray:
andersca@apple.com's avatar
andersca@apple.com committed
245 246 247 248 249
                hint = QMetaType::QVariantList;
                break;
        }
    }

250 251 252
    qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint;

    if (value == jsNull()
andersca@apple.com's avatar
andersca@apple.com committed
253
        && hint != QMetaType::QObjectStar
254 255 256
        && hint != QMetaType::VoidStar
        && hint != QMetaType::QString
        && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) {
andersca@apple.com's avatar
andersca@apple.com committed
257 258 259 260 261 262 263 264 265
        if (distance)
            *distance = -1;
        return QVariant();
    }

    QVariant ret;
    int dist = -1;
    switch (hint) {
        case QMetaType::Bool:
266
            if (type == Object && object->inherits(&BooleanObject::info))
267
                ret = QVariant(asBooleanObject(value)->internalValue().toBoolean(exec));
268
            else
269
                ret = QVariant(value.toBoolean(exec));
andersca@apple.com's avatar
andersca@apple.com committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
            if (type == Boolean)
                dist = 0;
            else
                dist = 10;
            break;

        case QMetaType::Int:
        case QMetaType::UInt:
        case QMetaType::Long:
        case QMetaType::ULong:
        case QMetaType::LongLong:
        case QMetaType::ULongLong:
        case QMetaType::Short:
        case QMetaType::UShort:
        case QMetaType::Float:
        case QMetaType::Double:
286
            ret = QVariant(value.toNumber(exec));
andersca@apple.com's avatar
andersca@apple.com committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
            ret.convert((QVariant::Type)hint);
            if (type == Number) {
                switch (hint) {
                case QMetaType::Double:
                    dist = 0;
                    break;
                case QMetaType::Float:
                    dist = 1;
                    break;
                case QMetaType::LongLong:
                case QMetaType::ULongLong:
                    dist = 2;
                    break;
                case QMetaType::Long:
                case QMetaType::ULong:
                    dist = 3;
                    break;
                case QMetaType::Int:
                case QMetaType::UInt:
                    dist = 4;
                    break;
                case QMetaType::Short:
                case QMetaType::UShort:
                    dist = 5;
                    break;
                    break;
                default:
                    dist = 10;
                    break;
                }
            } else {
                dist = 10;
            }
            break;

        case QMetaType::QChar:
            if (type == Number || type == Boolean) {
324
                ret = QVariant(QChar((ushort)value.toNumber(exec)));
andersca@apple.com's avatar
andersca@apple.com committed
325 326 327 328 329
                if (type == Boolean)
                    dist = 3;
                else
                    dist = 6;
            } else {
330
                UString str = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
331
                ret = QVariant(QChar(str.length() ? *(const ushort*)str.impl()->characters() : 0));
andersca@apple.com's avatar
andersca@apple.com committed
332 333 334 335 336 337 338 339
                if (type == String)
                    dist = 3;
                else
                    dist = 10;
            }
            break;

        case QMetaType::QString: {
340
            if (value.isUndefinedOrNull()) {
341 342 343 344
                if (distance)
                    *distance = 1;
                return QString();
            } else {
345
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
346
                ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()));
347 348 349 350 351
                if (type == String)
                    dist = 0;
                else
                    dist = 10;
            }
andersca@apple.com's avatar
andersca@apple.com committed
352 353 354
            break;
        }

355
        case QMetaType::QVariantMap:
356
            if (type == Object || type == Array || type == RTArray) {
andersca@apple.com's avatar
andersca@apple.com committed
357
                // Enumerate the contents of the object
358
                PropertyNameArray properties(exec);
359
                object->getPropertyNames(exec, properties);
andersca@apple.com's avatar
andersca@apple.com committed
360 361 362 363 364 365
                PropertyNameArray::const_iterator it = properties.begin();

                QVariantMap result;
                int objdist = 0;
                while(it != properties.end()) {
                    if (object->propertyIsEnumerable(exec, *it)) {
ggaren@apple.com's avatar
ggaren@apple.com committed
366
                        JSValue val = object->get(exec, *it);
367
                        QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
368 369
                        if (objdist >= 0) {
                            UString ustring = (*it).ustring();
barraclough@apple.com's avatar
barraclough@apple.com committed
370
                            QString id = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
371 372 373 374 375 376 377 378 379 380 381
                            result.insert(id, v);
                        }
                    }
                    ++it;
                }
                dist = 1;
                ret = QVariant(result);
            }
            break;

        case QMetaType::QVariantList:
382 383 384 385 386 387 388 389
            if (type == RTArray) {
                RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);

                QVariantList result;
                int len = rtarray->getLength();
                int objdist = 0;
                qConvDebug() << "converting a " << len << " length Array";
                for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
390
                    JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
391
                    result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit));
392 393 394 395 396 397 398 399 400 401
                    if (objdist == -1) {
                        qConvDebug() << "Failed converting element at index " << i;
                        break; // Failed converting a list entry, so fail the array
                    }
                }
                if (objdist != -1) {
                    dist = 5;
                    ret = QVariant(result);
                }
            } else if (type == Array) {
darin@apple.com's avatar
darin@apple.com committed
402
                JSArray* array = static_cast<JSArray*>(object);
andersca@apple.com's avatar
andersca@apple.com committed
403 404

                QVariantList result;
405
                int len = array->length();
andersca@apple.com's avatar
andersca@apple.com committed
406
                int objdist = 0;
407
                qConvDebug() << "converting a " << len << " length Array";
andersca@apple.com's avatar
andersca@apple.com committed
408
                for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
409
                    JSValue val = array->get(exec, i);
410
                    result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit));
411 412
                    if (objdist == -1) {
                        qConvDebug() << "Failed converting element at index " << i;
andersca@apple.com's avatar
andersca@apple.com committed
413
                        break; // Failed converting a list entry, so fail the array
414
                    }
andersca@apple.com's avatar
andersca@apple.com committed
415 416 417 418 419 420 421 422
                }
                if (objdist != -1) {
                    dist = 5;
                    ret = QVariant(result);
                }
            } else {
                // Make a single length array
                int objdist;
423
                qConvDebug() << "making a single length variantlist";
424
                QVariant var = convertValueToQVariant(exec, value, QMetaType::Void, &objdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
425
                if (objdist != -1) {
426 427
                    QVariantList result;
                    result << var;
andersca@apple.com's avatar
andersca@apple.com committed
428 429
                    ret = QVariant(result);
                    dist = 10;
430 431
                } else {
                    qConvDebug() << "failed making single length varlist";
andersca@apple.com's avatar
andersca@apple.com committed
432 433 434 435 436
                }
            }
            break;

        case QMetaType::QStringList: {
437 438 439 440 441 442
            if (type == RTArray) {
                RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);

                QStringList result;
                int len = rtarray->getLength();
                for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
443
                    JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
444
                    UString ustring = val.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
445
                    QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
446 447 448 449 450 451

                    result.append(qstring);
                }
                dist = 5;
                ret = QVariant(result);
            } else if (type == Array) {
darin@apple.com's avatar
darin@apple.com committed
452
                JSArray* array = static_cast<JSArray*>(object);
andersca@apple.com's avatar
andersca@apple.com committed
453 454

                QStringList result;
455
                int len = array->length();
andersca@apple.com's avatar
andersca@apple.com committed
456
                for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
457
                    JSValue val = array->get(exec, i);
458
                    UString ustring = val.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
459
                    QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
460 461 462 463 464 465 466

                    result.append(qstring);
                }
                dist = 5;
                ret = QVariant(result);
            } else {
                // Make a single length array
467
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
468
                QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
469 470 471 472 473 474 475 476 477
                QStringList result;
                result.append(qstring);
                ret = QVariant(result);
                dist = 10;
            }
            break;
        }

        case QMetaType::QByteArray: {
hausmann@webkit.org's avatar
hausmann@webkit.org committed
478 479 480 481 482 483
            if (type == JSByteArray) {
                WTF::ByteArray* arr = asByteArray(value)->storage();
                ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length()));
                dist = 0;
            } else {
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
484
                ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()).toLatin1());
hausmann@webkit.org's avatar
hausmann@webkit.org committed
485 486 487 488 489
                if (type == String)
                    dist = 5;
                else
                    dist = 10;
            }
andersca@apple.com's avatar
andersca@apple.com committed
490 491 492 493 494 495 496 497
            break;
        }

        case QMetaType::QDateTime:
        case QMetaType::QDate:
        case QMetaType::QTime:
            if (type == Date) {
                DateInstance* date = static_cast<DateInstance*>(object);
498
                GregorianDateTime gdt;
499
                msToGregorianDateTime(exec, date->internalNumber(), true, gdt);
andersca@apple.com's avatar
andersca@apple.com committed
500 501 502 503 504 505 506 507 508 509 510
                if (hint == QMetaType::QDateTime) {
                    ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
                    dist = 0;
                } else if (hint == QMetaType::QDate) {
                    ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
                    dist = 1;
                } else {
                    ret = QTime(gdt.hour + 1900, gdt.minute, gdt.second);
                    dist = 2;
                }
            } else if (type == Number) {
511
                double b = value.toNumber(exec);
512
                GregorianDateTime gdt;
513
                msToGregorianDateTime(exec, b, true, gdt);
andersca@apple.com's avatar
andersca@apple.com committed
514 515 516 517 518 519 520 521 522 523
                if (hint == QMetaType::QDateTime) {
                    ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
                    dist = 6;
                } else if (hint == QMetaType::QDate) {
                    ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
                    dist = 8;
                } else {
                    ret = QTime(gdt.hour, gdt.minute, gdt.second);
                    dist = 10;
                }
524
#ifndef QT_NO_DATESTRING
andersca@apple.com's avatar
andersca@apple.com committed
525
            } else if (type == String) {
526
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
527
                QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565

                if (hint == QMetaType::QDateTime) {
                    QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate);
                    if (!dt.isValid())
                        dt = QDateTime::fromString(qstring, Qt::TextDate);
                    if (!dt.isValid())
                        dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate);
                    if (!dt.isValid())
                        dt = QDateTime::fromString(qstring, Qt::LocaleDate);
                    if (dt.isValid()) {
                        ret = dt;
                        dist = 2;
                    }
                } else if (hint == QMetaType::QDate) {
                    QDate dt = QDate::fromString(qstring, Qt::ISODate);
                    if (!dt.isValid())
                        dt = QDate::fromString(qstring, Qt::TextDate);
                    if (!dt.isValid())
                        dt = QDate::fromString(qstring, Qt::SystemLocaleDate);
                    if (!dt.isValid())
                        dt = QDate::fromString(qstring, Qt::LocaleDate);
                    if (dt.isValid()) {
                        ret = dt;
                        dist = 3;
                    }
                } else {
                    QTime dt = QTime::fromString(qstring, Qt::ISODate);
                    if (!dt.isValid())
                        dt = QTime::fromString(qstring, Qt::TextDate);
                    if (!dt.isValid())
                        dt = QTime::fromString(qstring, Qt::SystemLocaleDate);
                    if (!dt.isValid())
                        dt = QTime::fromString(qstring, Qt::LocaleDate);
                    if (dt.isValid()) {
                        ret = dt;
                        dist = 3;
                    }
                }
566
#endif // QT_NO_DATESTRING
andersca@apple.com's avatar
andersca@apple.com committed
567 568 569 570 571
            }
            break;

        case QMetaType::QRegExp:
            if (type == RegExp) {
572
/*
darin@apple.com's avatar
darin@apple.com committed
573
                RegExpObject *re = static_cast<RegExpObject*>(object);
andersca@apple.com's avatar
andersca@apple.com committed
574 575
*/
                // Attempt to convert.. a bit risky
576
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
577
                QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
578 579

                // this is of the form '/xxxxxx/i'
580 581
                int firstSlash = qstring.indexOf(QLatin1Char('/'));
                int lastSlash = qstring.lastIndexOf(QLatin1Char('/'));
andersca@apple.com's avatar
andersca@apple.com committed
582 583 584 585 586
                if (firstSlash >=0 && lastSlash > firstSlash) {
                    QRegExp realRe;

                    realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1));

587
                    if (qstring.mid(lastSlash + 1).contains(QLatin1Char('i')))
andersca@apple.com's avatar
andersca@apple.com committed
588 589
                        realRe.setCaseSensitivity(Qt::CaseInsensitive);

590
                    ret = QVariant::fromValue(realRe);
andersca@apple.com's avatar
andersca@apple.com committed
591 592 593 594 595
                    dist = 0;
                } else {
                    qConvDebug() << "couldn't parse a JS regexp";
                }
            } else if (type == String) {
596
                UString ustring = value.toString(exec);
barraclough@apple.com's avatar
barraclough@apple.com committed
597
                QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
andersca@apple.com's avatar
andersca@apple.com committed
598 599 600

                QRegExp re(qstring);
                if (re.isValid()) {
601
                    ret = QVariant::fromValue(re);
andersca@apple.com's avatar
andersca@apple.com committed
602 603 604 605 606 607 608
                    dist = 10;
                }
            }
            break;

        case QMetaType::QObjectStar:
            if (type == QObj) {
609
                QtInstance* qtinst = QtInstance::getInstance(object);
andersca@apple.com's avatar
andersca@apple.com committed
610 611 612
                if (qtinst) {
                    if (qtinst->getObject()) {
                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
613
                        ret = QVariant::fromValue(qtinst->getObject());
andersca@apple.com's avatar
andersca@apple.com committed
614 615 616 617 618 619 620 621 622 623
                        qConvDebug() << ret;
                        dist = 0;
                    } else {
                        qConvDebug() << "can't convert deleted qobject";
                    }
                } else {
                    qConvDebug() << "wasn't a qtinstance";
                }
            } else if (type == Null) {
                QObject* nullobj = 0;
624
                ret = QVariant::fromValue(nullobj);
andersca@apple.com's avatar
andersca@apple.com committed
625 626 627 628 629 630 631 632
                dist = 0;
            } else {
                qConvDebug() << "previous type was not an object:" << type;
            }
            break;

        case QMetaType::VoidStar:
            if (type == QObj) {
633
                QtInstance* qtinst = QtInstance::getInstance(object);
andersca@apple.com's avatar
andersca@apple.com committed
634 635 636
                if (qtinst) {
                    if (qtinst->getObject()) {
                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
637
                        ret = QVariant::fromValue((void *)qtinst->getObject());
andersca@apple.com's avatar
andersca@apple.com committed
638 639 640 641 642 643 644 645 646
                        qConvDebug() << ret;
                        dist = 0;
                    } else {
                        qConvDebug() << "can't convert deleted qobject";
                    }
                } else {
                    qConvDebug() << "wasn't a qtinstance";
                }
            } else if (type == Null) {
647
                ret = QVariant::fromValue((void*)0);
andersca@apple.com's avatar
andersca@apple.com committed
648 649 650 651
                dist = 0;
            } else if (type == Number) {
                // I don't think that converting a double to a pointer is a wise
                // move.  Except maybe 0.
652
                qConvDebug() << "got number for void * - not converting, seems unsafe:" << value.toNumber(exec);
andersca@apple.com's avatar
andersca@apple.com committed
653 654 655 656 657 658 659 660 661
            } else {
                qConvDebug() << "void* - unhandled type" << type;
            }
            break;

        default:
            // Non const type ids
            if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>())
            {
662 663 664 665 666 667
                if (type == RTArray) {
                    RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);

                    QObjectList result;
                    int len = rtarray->getLength();
                    for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
668
                        JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
669
                        int itemdist = -1;
670
                        QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
671 672 673 674 675 676 677 678 679 680 681
                        if (itemdist >= 0)
                            result.append(item.value<QObject*>());
                        else
                            break;
                    }
                    // If we didn't fail conversion
                    if (result.count() == len) {
                        dist = 5;
                        ret = QVariant::fromValue(result);
                    }
                } else if (type == Array) {
682
                    JSObject* object = value.toObject(exec);
darin@apple.com's avatar
darin@apple.com committed
683
                    JSArray* array = static_cast<JSArray *>(object);
andersca@apple.com's avatar
andersca@apple.com committed
684
                    QObjectList result;
685
                    int len = array->length();
andersca@apple.com's avatar
andersca@apple.com committed
686
                    for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
687
                        JSValue val = array->get(exec, i);
andersca@apple.com's avatar
andersca@apple.com committed
688
                        int itemdist = -1;
689
                        QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703
                        if (itemdist >= 0)
                            result.append(item.value<QObject*>());
                        else
                            break;
                    }
                    // If we didn't fail conversion
                    if (result.count() == len) {
                        dist = 5;
                        ret = QVariant::fromValue(result);
                    }
                } else {
                    // Make a single length array
                    QObjectList result;
                    int itemdist = -1;
704
                    QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
705 706 707 708 709 710 711 712
                    if (itemdist >= 0) {
                        result.append(item.value<QObject*>());
                        dist = 10;
                        ret = QVariant::fromValue(result);
                    }
                }
                break;
            } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
713 714 715 716 717 718
                if (type == RTArray) {
                    RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);

                    QList<int> result;
                    int len = rtarray->getLength();
                    for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
719
                        JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
720
                        int itemdist = -1;
721
                        QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
722 723 724 725 726 727 728 729 730 731 732
                        if (itemdist >= 0)
                            result.append(item.value<int>());
                        else
                            break;
                    }
                    // If we didn't fail conversion
                    if (result.count() == len) {
                        dist = 5;
                        ret = QVariant::fromValue(result);
                    }
                } else if (type == Array) {
darin@apple.com's avatar
darin@apple.com committed
733
                    JSArray* array = static_cast<JSArray *>(object);
andersca@apple.com's avatar
andersca@apple.com committed
734 735

                    QList<int> result;
736
                    int len = array->length();
andersca@apple.com's avatar
andersca@apple.com committed
737
                    for (int i = 0; i < len; ++i) {
ggaren@apple.com's avatar
ggaren@apple.com committed
738
                        JSValue val = array->get(exec, i);
andersca@apple.com's avatar
andersca@apple.com committed
739
                        int itemdist = -1;
740
                        QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754
                        if (itemdist >= 0)
                            result.append(item.value<int>());
                        else
                            break;
                    }
                    // If we didn't fail conversion
                    if (result.count() == len) {
                        dist = 5;
                        ret = QVariant::fromValue(result);
                    }
                } else {
                    // Make a single length array
                    QList<int> result;
                    int itemdist = -1;
755
                    QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
andersca@apple.com's avatar
andersca@apple.com committed
756 757 758 759 760 761 762
                    if (itemdist >= 0) {
                        result.append(item.value<int>());
                        dist = 10;
                        ret = QVariant::fromValue(result);
                    }
                }
                break;
763 764
            } else if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(hint))) {
                ret = QtPixmapInstance::variantFromObject(object, static_cast<QMetaType::Type>(hint));
765 766 767
            } else if (hint == (QMetaType::Type) qMetaTypeId<QWebElement>()) {
                if (object && object->inherits(&JSHTMLElement::s_info))
                    ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSHTMLElement*>(object))->impl()));
768 769
                else if (object && object->inherits(&JSDocument::s_info))
                    ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSDocument*>(object))->impl()->documentElement()));
770 771
                else
                    ret = QVariant::fromValue<QWebElement>(QWebElement());
772 773 774
            } else if (hint == (QMetaType::Type) qMetaTypeId<QDRTNode>()) {
                if (object && object->inherits(&JSNode::s_info))
                    ret = QVariant::fromValue<QDRTNode>(QtDRTNodeRuntime::create((static_cast<JSNode*>(object))->impl()));
andersca@apple.com's avatar
andersca@apple.com committed
775
            } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) {
776
                if (value.isUndefinedOrNull()) {
777 778 779 780
                    if (distance)
                        *distance = 1;
                    return QVariant();
                } else {
781 782 783 784 785 786
                    if (type == Object) {
                        // Since we haven't really visited this object yet, we remove it
                        visitedObjects->remove(object);
                    }

                    // And then recurse with the autodetect flag
787
                    ret = convertValueToQVariant(exec, value, QMetaType::Void, distance, visitedObjects, recursionLimit);
788 789
                    dist = 10;
                }
andersca@apple.com's avatar
andersca@apple.com committed
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
                break;
            }

            dist = 10;
            break;
    }

    if (!ret.isValid())
        dist = -1;
    if (distance)
        *distance = dist;

    return ret;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
805
QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance)
806
{
807
    const int recursionLimit = 200;
808
    HashSet<JSObject*> visitedObjects;
809
    return convertValueToQVariant(exec, value, hint, distance, &visitedObjects, recursionLimit);
810 811
}

ggaren@apple.com's avatar
ggaren@apple.com committed
812
JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
andersca@apple.com's avatar
andersca@apple.com committed
813 814 815 816
{
    // Variants with QObject * can be isNull but not a null pointer
    // An empty QString variant is also null
    QMetaType::Type type = (QMetaType::Type) variant.userType();
817 818

    qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull();
andersca@apple.com's avatar
andersca@apple.com committed
819 820 821 822 823 824 825 826
    if (variant.isNull() &&
        type != QMetaType::QObjectStar &&
        type != QMetaType::VoidStar &&
        type != QMetaType::QWidgetStar &&
        type != QMetaType::QString) {
        return jsNull();
    }

ap@webkit.org's avatar
ap@webkit.org committed
827
    JSLock lock(SilenceAssertionsOnly);
ap@webkit.org's avatar
ap@webkit.org committed
828

andersca@apple.com's avatar
andersca@apple.com committed
829 830 831 832 833 834 835 836 837 838 839 840 841
    if (type == QMetaType::Bool)
        return jsBoolean(variant.toBool());

    if (type == QMetaType::Int ||
        type == QMetaType::UInt ||
        type == QMetaType::Long ||
        type == QMetaType::ULong ||
        type == QMetaType::LongLong ||
        type == QMetaType::ULongLong ||
        type == QMetaType::Short ||
        type == QMetaType::UShort ||
        type == QMetaType::Float ||
        type == QMetaType::Double)
842
        return jsNumber(variant.toDouble());
andersca@apple.com's avatar
andersca@apple.com committed
843 844 845 846 847

    if (type == QMetaType::QRegExp) {
        QRegExp re = variant.value<QRegExp>();

        if (re.isValid()) {
848
            UString uflags;
andersca@apple.com's avatar
andersca@apple.com committed
849 850
            if (re.caseSensitivity() == Qt::CaseInsensitive)
                uflags = "i"; // ### Can't do g or m
851 852 853

            UString pattern((UChar*)re.pattern().utf16(), re.pattern().length());

hausmann@webkit.org's avatar
hausmann@webkit.org committed
854
            RefPtr<JSC::RegExp> regExp = JSC::RegExp::create(&exec->globalData(), pattern, uflags);
855
            if (regExp->isValid())
oliver@apple.com's avatar
oliver@apple.com committed
856
                return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release());
857 858
            else
                return jsNull();
andersca@apple.com's avatar
andersca@apple.com committed
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
        }
    }

    if (type == QMetaType::QDateTime ||
        type == QMetaType::QDate ||
        type == QMetaType::QTime) {

        QDate date = QDate::currentDate();
        QTime time(0,0,0); // midnight

        if (type == QMetaType::QDate)
            date = variant.value<QDate>();
        else if (type == QMetaType::QTime)
            time = variant.value<QTime>();
        else {
            QDateTime dt = variant.value<QDateTime>().toLocalTime();
            date = dt.date();
            time = dt.time();
        }

        // Dates specified this way are in local time (we convert DateTimes above)
880
        GregorianDateTime dt;
881 882 883 884 885 886 887
        dt.year = date.year() - 1900;
        dt.month = date.month() - 1;
        dt.monthDay = date.day();
        dt.hour = time.hour();
        dt.minute = time.minute();
        dt.second = time.second();
        dt.isDST = -1;
888
        double ms = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false);
889

890
        return new (exec) DateInstance(exec, trunc(ms));
andersca@apple.com's avatar
andersca@apple.com committed
891 892 893
    }

    if (type == QMetaType::QByteArray) {
hausmann@webkit.org's avatar
hausmann@webkit.org committed
894 895
        QByteArray qtByteArray = variant.value<QByteArray>();
        WTF::RefPtr<WTF::ByteArray> wtfByteArray = WTF::ByteArray::create(qtByteArray.length());
896
        memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
897
        return new (exec) JSC::JSByteArray(exec, JSC::JSByteArray::createStructure(jsNull()), wtfByteArray.get());
andersca@apple.com's avatar
andersca@apple.com committed
898 899 900 901
    }

    if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
        QObject* obj = variant.value<QObject*>();
902 903
        if (!obj)
            return jsNull();
hausmann@webkit.org's avatar
hausmann@webkit.org committed
904
        return QtInstance::getQtInstance(obj, root, QScriptEngine::QtOwnership)->createRuntimeObject(exec);
andersca@apple.com's avatar
andersca@apple.com committed
905 906
    }

907
    if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type())))
908
        return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant);
909

910 911 912 913 914 915 916 917 918 919 920
    if (type == qMetaTypeId<QWebElement>()) {
        if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
            return jsUndefined();

        Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
        if (!document)
            return jsUndefined();

        return toJS(exec, toJSDOMGlobalObject(document, exec), QtWebElementRuntime::get(variant.value<QWebElement>()));
    }

921 922 923 924 925 926 927 928 929 930 931
    if (type == qMetaTypeId<QDRTNode>()) {
        if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
            return jsUndefined();

        Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
        if (!document)
            return jsUndefined();

        return toJS(exec, toJSDOMGlobalObject(document, exec), QtDRTNodeRuntime::get(variant.value<QDRTNode>()));
    }

andersca@apple.com's avatar
andersca@apple.com committed
932 933
    if (type == QMetaType::QVariantMap) {
        // create a new object, and stuff properties into it
934
        JSObject* ret = constructEmptyObject(exec);
andersca@apple.com's avatar
andersca@apple.com committed
935 936 937 938
        QVariantMap map = variant.value<QVariantMap>();
        QVariantMap::const_iterator i = map.constBegin();
        while (i != map.constEnd()) {
            QString s = i.key();
939
            JSValue val = convertQVariantToValue(exec, root.get(), i.value());
940 941
            if (val) {
                PutPropertySlot slot;
942
                ret->put(exec, Identifier(exec, reinterpret_cast_ptr<const UChar *>(s.constData()), s.length()), val, slot);
943 944
                // ### error case?
            }
andersca@apple.com's avatar
andersca@apple.com committed
945 946 947 948 949 950 951 952 953
            ++i;
        }

        return ret;
    }

    // List types
    if (type == QMetaType::QVariantList) {
        QVariantList vl = variant.toList();
954
        qConvDebug() << "got a " << vl.count() << " length list:" << vl;
ap@webkit.org's avatar
ap@webkit.org committed
955
        return new (exec) RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
andersca@apple.com's avatar
andersca@apple.com committed
956 957
    } else if (type == QMetaType::QStringList) {
        QStringList sl = variant.value<QStringList>();
ap@webkit.org's avatar
ap@webkit.org committed
958
        return new (exec) RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root));
andersca@apple.com's avatar
andersca@apple.com committed
959 960
    } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
        QObjectList ol= variant.value<QObjectList>();
ap@webkit.org's avatar
ap@webkit.org committed
961
        return new (exec) RuntimeArray(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
andersca@apple.com's avatar
andersca@apple.com committed
962 963
    } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
        QList<int> il= variant.value<QList<int> >();
ap@webkit.org's avatar
ap@webkit.org committed
964
        return new (exec) RuntimeArray(exec, new QtArray<int>(il, QMetaType::Int, root));
andersca@apple.com's avatar
andersca@apple.com committed
965 966 967 968 969 970 971 972 973 974 975
    }

    if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
        QVariant real = variant.value<QVariant>();
        qConvDebug() << "real variant is:" << real;
        return convertQVariantToValue(exec, root, real);
    }

    qConvDebug() << "fallback path for" << variant << variant.userType();

    QString string = variant.toString();
darin@apple.com's avatar
darin@apple.com committed
976
    UString ustring((UChar*)string.utf16(), string.length());
ap@webkit.org's avatar
ap@webkit.org committed
977
    return jsString(exec, ustring);
andersca@apple.com's avatar
andersca@apple.com committed
978 979 980 981 982 983 984 985
}

// ===============

// Qt-like macros
#define QW_D(Class) Class##Data* d = d_func()
#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()

986 987 988
const ClassInfo QtRuntimeMethod::s_info = { "QtRuntimeMethod", 0, 0, 0 };

QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst)
oliver@apple.com's avatar
oliver@apple.com committed
989
    : InternalFunction(&exec->globalData(), exec->lexicalGlobalObject(), deprecatedGetDOMStructure<QtRuntimeMethod>(exec), ident)
andersca@apple.com's avatar
andersca@apple.com committed
990 991 992 993 994 995 996 997
    , d_ptr(dd)
{
    QW_D(QtRuntimeMethod);
    d->m_instance = inst;
}

QtRuntimeMethod::~QtRuntimeMethod()
{
hausmann@webkit.org's avatar
hausmann@webkit.org committed
998 999
    QW_D(QtRuntimeMethod);
    d->m_instance->removeCachedMethod(this);
andersca@apple.com's avatar
andersca@apple.com committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178