Home | History | Annotate | Download | only in qt
      1 /*
      2  * Copyright (C) 2008 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 Lesser 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  *  Lesser General Public License for more details.
     13  *
     14  *  You should have received a copy of the GNU Lesser General Public
     15  *  License along with this library; if not, write to the Free Software
     16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     17  *
     18  */
     19 
     20 #include "config.h"
     21 #include "qt_instance.h"
     22 
     23 #include "Error.h"
     24 #include "JSDOMBinding.h"
     25 #include "JSGlobalObject.h"
     26 #include "JSLock.h"
     27 #include "ObjectPrototype.h"
     28 #include "PropertyNameArray.h"
     29 #include "qt_class.h"
     30 #include "qt_runtime.h"
     31 #include "runtime_object.h"
     32 #include "runtime/FunctionPrototype.h"
     33 
     34 #include <qdebug.h>
     35 #include <qhash.h>
     36 #include <qmetaobject.h>
     37 #include <qmetatype.h>
     38 #include <qwebelement.h>
     39 
     40 namespace JSC {
     41 namespace Bindings {
     42 
     43 // Cache QtInstances
     44 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
     45 static QObjectInstanceMap cachedInstances;
     46 
     47 // Derived RuntimeObject
     48 class QtRuntimeObject : public RuntimeObject {
     49 public:
     50     QtRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>);
     51 
     52     static const ClassInfo s_info;
     53 
     54     virtual void markChildren(MarkStack& markStack)
     55     {
     56         RuntimeObject::markChildren(markStack);
     57         QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
     58         if (instance)
     59             instance->markAggregate(markStack);
     60     }
     61 
     62     static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
     63     {
     64         return Structure::create(globalData, prototype, TypeInfo(ObjectType,  StructureFlags), AnonymousSlotCount, &s_info);
     65     }
     66 
     67 protected:
     68     static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren;
     69 };
     70 
     71 const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0 };
     72 
     73 QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance)
     74     : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec), instance)
     75 {
     76 }
     77 
     78 // QtInstance
     79 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
     80     : Instance(rootObject)
     81     , m_class(0)
     82     , m_object(o)
     83     , m_hashkey(o)
     84     , m_ownership(ownership)
     85 {
     86     // This is a good place to register Qt metatypes that are in the QtWebKit module, as this is class will initialize if we have a QObject bridge.
     87     qRegisterMetaType<QWebElement>();
     88 }
     89 
     90 QtInstance::~QtInstance()
     91 {
     92     JSLock lock(SilenceAssertionsOnly);
     93 
     94     cachedInstances.remove(m_hashkey);
     95 
     96     // clean up (unprotect from gc) the JSValues we've created
     97     m_methods.clear();
     98 
     99     qDeleteAll(m_fields);
    100     m_fields.clear();
    101 
    102     if (m_object) {
    103         switch (m_ownership) {
    104         case QScriptEngine::QtOwnership:
    105             break;
    106         case QScriptEngine::AutoOwnership:
    107             if (m_object->parent())
    108                 break;
    109             // fall through!
    110         case QScriptEngine::ScriptOwnership:
    111             delete m_object;
    112             break;
    113         }
    114     }
    115 }
    116 
    117 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
    118 {
    119     JSLock lock(SilenceAssertionsOnly);
    120 
    121     foreach (QtInstance* instance, cachedInstances.values(o))
    122         if (instance->rootObject() == rootObject) {
    123             // The garbage collector removes instances, but it may happen that the wrapped
    124             // QObject dies before the gc kicks in. To handle that case we have to do an additional
    125             // check if to see if the instance's wrapped object is still alive. If it isn't, then
    126             // we have to create a new wrapper.
    127             if (!instance->getObject())
    128                 cachedInstances.remove(instance->hashKey());
    129             else
    130                 return instance;
    131         }
    132 
    133     RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
    134     cachedInstances.insert(o, ret.get());
    135 
    136     return ret.release();
    137 }
    138 
    139 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
    140 {
    141     return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
    142 }
    143 
    144 void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
    145 {
    146     object->JSObject::put(exec, propertyName, value, slot);
    147 }
    148 
    149 void QtInstance::removeCachedMethod(JSObject* method)
    150 {
    151     if (m_defaultMethod.get() == method)
    152         m_defaultMethod.clear();
    153 
    154     for (QHash<QByteArray, WriteBarrier<JSObject> >::Iterator it = m_methods.begin(), end = m_methods.end(); it != end; ++it) {
    155         if (it.value().get() == method) {
    156             m_methods.erase(it);
    157             return;
    158         }
    159     }
    160 }
    161 
    162 QtInstance* QtInstance::getInstance(JSObject* object)
    163 {
    164     if (!object)
    165         return 0;
    166     if (!object->inherits(&QtRuntimeObject::s_info))
    167         return 0;
    168     return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance());
    169 }
    170 
    171 Class* QtInstance::getClass() const
    172 {
    173     if (!m_class) {
    174         if (!m_object)
    175             return 0;
    176         m_class = QtClass::classForObject(m_object);
    177     }
    178     return m_class;
    179 }
    180 
    181 RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec)
    182 {
    183     JSLock lock(SilenceAssertionsOnly);
    184     m_methods.clear();
    185     return new (exec) QtRuntimeObject(exec, exec->lexicalGlobalObject(), this);
    186 }
    187 
    188 void QtInstance::markAggregate(MarkStack& markStack)
    189 {
    190     if (m_defaultMethod)
    191         markStack.append(&m_defaultMethod);
    192     for (QHash<QByteArray, WriteBarrier<JSObject> >::Iterator it = m_methods.begin(), end = m_methods.end(); it != end; ++it)
    193         markStack.append(&it.value());
    194 }
    195 
    196 void QtInstance::begin()
    197 {
    198     // Do nothing.
    199 }
    200 
    201 void QtInstance::end()
    202 {
    203     // Do nothing.
    204 }
    205 
    206 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
    207 {
    208     // This is the enumerable properties, so put:
    209     // properties
    210     // dynamic properties
    211     // slots
    212     QObject* obj = getObject();
    213     if (obj) {
    214         const QMetaObject* meta = obj->metaObject();
    215 
    216         int i;
    217         for (i = 0; i < meta->propertyCount(); i++) {
    218             QMetaProperty prop = meta->property(i);
    219             if (prop.isScriptable())
    220                 array.add(Identifier(exec, prop.name()));
    221         }
    222 
    223 #ifndef QT_NO_PROPERTIES
    224         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
    225         foreach (const QByteArray& ba, dynProps)
    226             array.add(Identifier(exec, ba.constData()));
    227 #endif
    228 
    229         const int methodCount = meta->methodCount();
    230         for (i = 0; i < methodCount; i++) {
    231             QMetaMethod method = meta->method(i);
    232             if (method.access() != QMetaMethod::Private)
    233                 array.add(Identifier(exec, method.signature()));
    234         }
    235     }
    236 }
    237 
    238 JSValue QtInstance::getMethod(ExecState* exec, const Identifier& propertyName)
    239 {
    240     if (!getClass())
    241         return jsNull();
    242     MethodList methodList = m_class->methodsNamed(propertyName, this);
    243     return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), WebCore::deprecatedGetDOMStructure<RuntimeMethod>(exec), propertyName, methodList);
    244 }
    245 
    246 JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*)
    247 {
    248     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
    249     return jsUndefined();
    250 }
    251 
    252 JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
    253 {
    254     if (hint == PreferString)
    255         return stringValue(exec);
    256     if (hint == PreferNumber)
    257         return numberValue(exec);
    258     return valueOf(exec);
    259 }
    260 
    261 JSValue QtInstance::stringValue(ExecState* exec) const
    262 {
    263     QObject* obj = getObject();
    264     if (!obj)
    265         return jsNull();
    266 
    267     // Hmm.. see if there is a toString defined
    268     QByteArray buf;
    269     bool useDefault = true;
    270     getClass();
    271     if (m_class) {
    272         // Cheat and don't use the full name resolution
    273         int index = obj->metaObject()->indexOfMethod("toString()");
    274         if (index >= 0) {
    275             QMetaMethod m = obj->metaObject()->method(index);
    276             // Check to see how much we can call it
    277             if (m.access() != QMetaMethod::Private
    278                 && m.methodType() != QMetaMethod::Signal
    279                 && m.parameterTypes().isEmpty()) {
    280                 const char* retsig = m.typeName();
    281                 if (retsig && *retsig) {
    282                     QVariant ret(QMetaType::type(retsig), (void*)0);
    283                     void * qargs[1];
    284                     qargs[0] = ret.data();
    285 
    286                     if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
    287                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
    288                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
    289                             useDefault = false;
    290                         }
    291                     }
    292                 }
    293             }
    294         }
    295     }
    296 
    297     if (useDefault) {
    298         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
    299         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
    300         QString str = QString::fromUtf8("%0(name = \"%1\")")
    301                       .arg(QLatin1String(meta->className())).arg(name);
    302 
    303         buf = str.toLatin1();
    304     }
    305     return jsString(exec, buf.constData());
    306 }
    307 
    308 JSValue QtInstance::numberValue(ExecState*) const
    309 {
    310     return jsNumber(0);
    311 }
    312 
    313 JSValue QtInstance::booleanValue() const
    314 {
    315     // ECMA 9.2
    316     return jsBoolean(getObject());
    317 }
    318 
    319 JSValue QtInstance::valueOf(ExecState* exec) const
    320 {
    321     return stringValue(exec);
    322 }
    323 
    324 // In qt_runtime.cpp
    325 JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
    326 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
    327 
    328 QByteArray QtField::name() const
    329 {
    330     if (m_type == MetaProperty)
    331         return m_property.name();
    332     if (m_type == ChildObject && m_childObject)
    333         return m_childObject->objectName().toLatin1();
    334 #ifndef QT_NO_PROPERTIES
    335     if (m_type == DynamicProperty)
    336         return m_dynamicProperty;
    337 #endif
    338     return QByteArray(); // deleted child object
    339 }
    340 
    341 JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
    342 {
    343     const QtInstance* instance = static_cast<const QtInstance*>(inst);
    344     QObject* obj = instance->getObject();
    345 
    346     if (obj) {
    347         QVariant val;
    348         if (m_type == MetaProperty) {
    349             if (m_property.isReadable())
    350                 val = m_property.read(obj);
    351             else
    352                 return jsUndefined();
    353         } else if (m_type == ChildObject)
    354             val = QVariant::fromValue((QObject*) m_childObject);
    355 #ifndef QT_NO_PROPERTIES
    356         else if (m_type == DynamicProperty)
    357             val = obj->property(m_dynamicProperty);
    358 #endif
    359         return convertQVariantToValue(exec, inst->rootObject(), val);
    360     }
    361     QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
    362     return throwError(exec, createError(exec, msg.toLatin1().constData()));
    363 }
    364 
    365 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
    366 {
    367     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
    368         return;
    369 
    370     const QtInstance* instance = static_cast<const QtInstance*>(inst);
    371     QObject* obj = instance->getObject();
    372     if (obj) {
    373         QMetaType::Type argtype = QMetaType::Void;
    374         if (m_type == MetaProperty)
    375             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
    376 
    377         // dynamic properties just get any QVariant
    378         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
    379         if (m_type == MetaProperty) {
    380             if (m_property.isWritable())
    381                 m_property.write(obj, val);
    382         }
    383 #ifndef QT_NO_PROPERTIES
    384         else if (m_type == DynamicProperty)
    385             obj->setProperty(m_dynamicProperty.constData(), val);
    386 #endif
    387     } else {
    388         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
    389         throwError(exec, createError(exec, msg.toLatin1().constData()));
    390     }
    391 }
    392 
    393 
    394 }
    395 }
    396