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