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