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