WebCore/bridge/qt/qt_instance.cpp
changeset 0 4f2f89ce4247
child 1 9d347b658349
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/bridge/qt/qt_instance.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ *  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_instance.h"
+
+#include "Error.h"
+#include "JSDOMBinding.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "ObjectPrototype.h"
+#include "PropertyNameArray.h"
+#include "qt_class.h"
+#include "qt_runtime.h"
+#include "runtime_object.h"
+
+#include <qdebug.h>
+#include <qhash.h>
+#include <qmetaobject.h>
+#include <qmetatype.h>
+
+namespace JSC {
+namespace Bindings {
+
+// Cache QtInstances
+typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
+static QObjectInstanceMap cachedInstances;
+
+// Derived RuntimeObject
+class QtRuntimeObject : public RuntimeObject {
+public:
+    QtRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>);
+
+    static const ClassInfo s_info;
+
+    virtual void markChildren(MarkStack& markStack)
+    {
+        RuntimeObject::markChildren(markStack);
+        QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
+        if (instance)
+            instance->markAggregate(markStack);
+    }
+
+    static PassRefPtr<Structure> createStructure(JSValue prototype)
+    {
+        return Structure::create(prototype, TypeInfo(ObjectType,  StructureFlags), AnonymousSlotCount);
+    }
+
+protected:
+    static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren;
+
+private:
+    virtual const ClassInfo* classInfo() const { return &s_info; }
+};
+
+const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0 };
+
+QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance)
+    : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec), instance)
+{
+}
+
+// QtInstance
+QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
+    : Instance(rootObject)
+    , m_class(0)
+    , m_object(o)
+    , m_hashkey(o)
+    , m_defaultMethod(0)
+    , m_ownership(ownership)
+{
+}
+
+QtInstance::~QtInstance()
+{
+    JSLock lock(SilenceAssertionsOnly);
+
+    cachedInstances.remove(m_hashkey);
+
+    // clean up (unprotect from gc) the JSValues we've created
+    m_methods.clear();
+
+    qDeleteAll(m_fields);
+    m_fields.clear();
+
+    if (m_object) {
+        switch (m_ownership) {
+        case QScriptEngine::QtOwnership:
+            break;
+        case QScriptEngine::AutoOwnership:
+            if (m_object->parent())
+                break;
+            // fall through!
+        case QScriptEngine::ScriptOwnership:
+            delete m_object;
+            break;
+        }
+    }
+}
+
+PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
+{
+    JSLock lock(SilenceAssertionsOnly);
+
+    foreach (QtInstance* instance, cachedInstances.values(o))
+        if (instance->rootObject() == rootObject) {
+            // The garbage collector removes instances, but it may happen that the wrapped
+            // QObject dies before the gc kicks in. To handle that case we have to do an additional
+            // check if to see if the instance's wrapped object is still alive. If it isn't, then
+            // we have to create a new wrapper.
+            if (!instance->getObject())
+                cachedInstances.remove(instance->hashKey());
+            else
+                return instance;
+        }
+
+    RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
+    cachedInstances.insert(o, ret.get());
+
+    return ret.release();
+}
+
+bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+    return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
+}
+
+void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
+{
+    object->JSObject::put(exec, propertyName, value, slot);
+}
+
+void QtInstance::removeCachedMethod(JSObject* method)
+{
+    if (m_defaultMethod == method)
+        m_defaultMethod = 0;
+
+    for (QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(),
+        end = m_methods.end(); it != end; ++it)
+        if (it.value() == method) {
+            m_methods.erase(it);
+            return;
+        }
+}
+
+QtInstance* QtInstance::getInstance(JSObject* object)
+{
+    if (!object)
+        return 0;
+    if (!object->inherits(&QtRuntimeObject::s_info))
+        return 0;
+    return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance());
+}
+
+Class* QtInstance::getClass() const
+{
+    if (!m_class) {
+        if (!m_object)
+            return 0;
+        m_class = QtClass::classForObject(m_object);
+    }
+    return m_class;
+}
+
+RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec)
+{
+    JSLock lock(SilenceAssertionsOnly);
+    return new (exec) QtRuntimeObject(exec, exec->lexicalGlobalObject(), this);
+}
+
+void QtInstance::markAggregate(MarkStack& markStack)
+{
+    if (m_defaultMethod)
+        markStack.append(m_defaultMethod);
+    foreach (JSObject* val, m_methods.values()) {
+        if (val)
+            markStack.append(val);
+    }
+}
+
+void QtInstance::begin()
+{
+    // Do nothing.
+}
+
+void QtInstance::end()
+{
+    // Do nothing.
+}
+
+void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
+{
+    // This is the enumerable properties, so put:
+    // properties
+    // dynamic properties
+    // slots
+    QObject* obj = getObject();
+    if (obj) {
+        const QMetaObject* meta = obj->metaObject();
+
+        int i;
+        for (i = 0; i < meta->propertyCount(); i++) {
+            QMetaProperty prop = meta->property(i);
+            if (prop.isScriptable())
+                array.add(Identifier(exec, prop.name()));
+        }
+
+#ifndef QT_NO_PROPERTIES
+        QList<QByteArray> dynProps = obj->dynamicPropertyNames();
+        foreach (const QByteArray& ba, dynProps)
+            array.add(Identifier(exec, ba.constData()));
+#endif
+
+        const int methodCount = meta->methodCount();
+        for (i = 0; i < methodCount; i++) {
+            QMetaMethod method = meta->method(i);
+            if (method.access() != QMetaMethod::Private)
+                array.add(Identifier(exec, method.signature()));
+        }
+    }
+}
+
+JSValue QtInstance::getMethod(ExecState* exec, const Identifier& propertyName)
+{
+    if (!getClass())
+        return jsNull();
+    MethodList methodList = m_class->methodsNamed(propertyName, this);
+    return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList);
+}
+
+JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*)
+{
+    // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
+    return jsUndefined();
+}
+
+JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
+{
+    if (hint == PreferString)
+        return stringValue(exec);
+    if (hint == PreferNumber)
+        return numberValue(exec);
+    return valueOf(exec);
+}
+
+JSValue QtInstance::stringValue(ExecState* exec) const
+{
+    QObject* obj = getObject();
+    if (!obj)
+        return jsNull();
+
+    // Hmm.. see if there is a toString defined
+    QByteArray buf;
+    bool useDefault = true;
+    getClass();
+    if (m_class) {
+        // Cheat and don't use the full name resolution
+        int index = obj->metaObject()->indexOfMethod("toString()");
+        if (index >= 0) {
+            QMetaMethod m = obj->metaObject()->method(index);
+            // Check to see how much we can call it
+            if (m.access() != QMetaMethod::Private
+                && m.methodType() != QMetaMethod::Signal
+                && m.parameterTypes().isEmpty()) {
+                const char* retsig = m.typeName();
+                if (retsig && *retsig) {
+                    QVariant ret(QMetaType::type(retsig), (void*)0);
+                    void * qargs[1];
+                    qargs[0] = ret.data();
+
+                    if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
+                        if (ret.isValid() && ret.canConvert(QVariant::String)) {
+                            buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
+                            useDefault = false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (useDefault) {
+        const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
+        QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
+        QString str = QString::fromUtf8("%0(name = \"%1\")")
+                      .arg(QLatin1String(meta->className())).arg(name);
+
+        buf = str.toLatin1();
+    }
+    return jsString(exec, buf.constData());
+}
+
+JSValue QtInstance::numberValue(ExecState* exec) const
+{
+    return jsNumber(exec, 0);
+}
+
+JSValue QtInstance::booleanValue() const
+{
+    // ECMA 9.2
+    return jsBoolean(getObject());
+}
+
+JSValue QtInstance::valueOf(ExecState* exec) const
+{
+    return stringValue(exec);
+}
+
+// In qt_runtime.cpp
+JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
+QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
+
+QByteArray QtField::name() const
+{
+    if (m_type == MetaProperty)
+        return m_property.name();
+    if (m_type == ChildObject && m_childObject)
+        return m_childObject->objectName().toLatin1();
+#ifndef QT_NO_PROPERTIES
+    if (m_type == DynamicProperty)
+        return m_dynamicProperty;
+#endif
+    return QByteArray(); // deleted child object
+}
+
+JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
+{
+    const QtInstance* instance = static_cast<const QtInstance*>(inst);
+    QObject* obj = instance->getObject();
+
+    if (obj) {
+        QVariant val;
+        if (m_type == MetaProperty) {
+            if (m_property.isReadable())
+                val = m_property.read(obj);
+            else
+                return jsUndefined();
+        } else if (m_type == ChildObject)
+            val = QVariant::fromValue((QObject*) m_childObject);
+#ifndef QT_NO_PROPERTIES
+        else if (m_type == DynamicProperty)
+            val = obj->property(m_dynamicProperty);
+#endif
+        return convertQVariantToValue(exec, inst->rootObject(), val);
+    }
+    QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
+    return throwError(exec, createError(exec, msg.toLatin1().constData()));
+}
+
+void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
+{
+    if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
+        return;
+
+    const QtInstance* instance = static_cast<const QtInstance*>(inst);
+    QObject* obj = instance->getObject();
+    if (obj) {
+        QMetaType::Type argtype = QMetaType::Void;
+        if (m_type == MetaProperty)
+            argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
+
+        // dynamic properties just get any QVariant
+        QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
+        if (m_type == MetaProperty) {
+            if (m_property.isWritable())
+                m_property.write(obj, val);
+        }
+#ifndef QT_NO_PROPERTIES
+        else if (m_type == DynamicProperty)
+            obj->setProperty(m_dynamicProperty.constData(), val);
+#endif
+    } else {
+        QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
+        throwError(exec, createError(exec, msg.toLatin1().constData()));
+    }
+}
+
+
+}
+}