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