2008-09-23 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebCore / bridge / qt / qt_instance.cpp
1 /*
2  * Copyright (C) 2006 Trolltech ASA
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 "ArgList.h"
24 #include "JSDOMBinding.h"
25 #include "JSGlobalObject.h"
26 #include "JSLock.h"
27 #include "qt_class.h"
28 #include "qt_runtime.h"
29 #include "PropertyNameArray.h"
30 #include "runtime_object.h"
31 #include "ObjectPrototype.h"
32 #include "Error.h"
33
34 #include <qmetaobject.h>
35 #include <qdebug.h>
36 #include <qmetatype.h>
37 #include <qhash.h>
38
39 namespace JSC {
40 namespace Bindings {
41
42 // Cache QtInstances
43 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
44 static QObjectInstanceMap cachedInstances;
45
46 // Cache JSObjects
47 typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap;
48 static InstanceJSObjectMap cachedObjects;
49
50 // Derived RuntimeObject
51 class QtRuntimeObjectImp : public RuntimeObjectImp {
52     public:
53         QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
54         ~QtRuntimeObjectImp();
55         virtual void invalidate();
56
57         virtual void mark() {
58             QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
59             if (instance)
60                 instance->mark();
61             RuntimeObjectImp::mark();
62         }
63
64     protected:
65         void removeFromCache();
66 };
67
68 QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
69     : RuntimeObjectImp(exec, WebCore::getDOMStructure<QtRuntimeObjectImp>(exec), instance)
70 {
71 }
72
73 QtRuntimeObjectImp::~QtRuntimeObjectImp()
74 {
75     removeFromCache();
76 }
77
78 void QtRuntimeObjectImp::invalidate()
79 {
80     removeFromCache();
81     RuntimeObjectImp::invalidate();
82 }
83
84 void QtRuntimeObjectImp::removeFromCache()
85 {
86     JSLock lock(false);
87     QtInstance* key = cachedObjects.key(this);
88     if (key)
89         cachedObjects.remove(key);
90 }
91
92 // QtInstance
93 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
94     : Instance(rootObject)
95     , m_class(0)
96     , m_object(o)
97     , m_hashkey(o)
98     , m_defaultMethod(0)
99 {
100 }
101
102 QtInstance::~QtInstance()
103 {
104     JSLock lock(false);
105
106     cachedObjects.remove(this);
107     cachedInstances.remove(m_hashkey);
108
109     // clean up (unprotect from gc) the JSValues we've created
110     m_methods.clear();
111
112     foreach(QtField* f, m_fields.values()) {
113         delete f;
114     }
115     m_fields.clear();
116 }
117
118 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
119 {
120     JSLock lock(false);
121
122     foreach(QtInstance* instance, cachedInstances.values(o)) {
123         if (instance->rootObject() == rootObject)
124             return instance;
125     }
126
127     RefPtr<QtInstance> ret = QtInstance::create(o, rootObject);
128     cachedInstances.insert(o, ret.get());
129
130     return ret.release();
131 }
132
133 RuntimeObjectImp* QtInstance::getRuntimeObject(ExecState* exec, PassRefPtr<QtInstance> instance)
134 {
135     JSLock lock(false);
136     QtInstance* qtInstance = instance.get();
137     RuntimeObjectImp* ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance));
138     if (!ret) {
139         ret = new (exec) QtRuntimeObjectImp(exec, instance);
140         cachedObjects.insert(qtInstance, ret);
141         ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance));
142     }
143     return ret;
144 }
145
146 Class* QtInstance::getClass() const
147 {
148     if (!m_class)
149         m_class = QtClass::classForObject(m_object);
150     return m_class;
151 }
152
153 void QtInstance::mark()
154 {
155     if (m_defaultMethod)
156         m_defaultMethod->mark();
157     foreach(JSValue* val, m_methods.values()) {
158         if (val && !val->marked())
159             val->mark();
160     }
161     foreach(JSValue* val, m_children.values()) {
162         if (val && !val->marked())
163             val->mark();
164     }
165 }
166
167 void QtInstance::begin()
168 {
169     // Do nothing.
170 }
171
172 void QtInstance::end()
173 {
174     // Do nothing.
175 }
176
177 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
178 {
179     // This is the enumerable properties, so put:
180     // properties
181     // dynamic properties
182     // slots
183     QObject* obj = getObject();
184     if (obj) {
185         const QMetaObject* meta = obj->metaObject();
186
187         int i;
188         for (i=0; i < meta->propertyCount(); i++) {
189             QMetaProperty prop = meta->property(i);
190             if (prop.isScriptable()) {
191                 array.add(Identifier(exec, prop.name()));
192             }
193         }
194
195         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
196         foreach(QByteArray ba, dynProps) {
197             array.add(Identifier(exec, ba.constData()));
198         }
199
200         for (i=0; i < meta->methodCount(); i++) {
201             QMetaMethod method = meta->method(i);
202             if (method.access() != QMetaMethod::Private) {
203                 array.add(Identifier(exec, method.signature()));
204             }
205         }
206     }
207 }
208
209 JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
210 {
211     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
212     return jsUndefined();
213 }
214
215
216 JSValue* QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
217 {
218     if (hint == JSValue::PreferString)
219         return stringValue(exec);
220     if (hint == JSValue::PreferNumber)
221         return numberValue(exec);
222     return valueOf(exec);
223 }
224
225 JSValue* QtInstance::stringValue(ExecState* exec) const
226 {
227     // Hmm.. see if there is a toString defined
228     QByteArray buf;
229     bool useDefault = true;
230     getClass();
231     QObject* obj = getObject();
232     if (m_class && obj) {
233         // Cheat and don't use the full name resolution
234         int index = obj->metaObject()->indexOfMethod("toString()");
235         if (index >= 0) {
236             QMetaMethod m = obj->metaObject()->method(index);
237             // Check to see how much we can call it
238             if (m.access() != QMetaMethod::Private
239                 && m.methodType() != QMetaMethod::Signal
240                 && m.parameterTypes().count() == 0) {
241                 const char* retsig = m.typeName();
242                 if (retsig && *retsig) {
243                     QVariant ret(QMetaType::type(retsig), (void*)0);
244                     void * qargs[1];
245                     qargs[0] = ret.data();
246
247                     if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
248                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
249                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
250                             useDefault = false;
251                         }
252                     }
253                 }
254             }
255         }
256     }
257
258     if (useDefault) {
259         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
260         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
261         QString str = QString::fromUtf8("%0(name = \"%1\")")
262                       .arg(QLatin1String(meta->className())).arg(name);
263
264         buf = str.toLatin1();
265     }
266     return jsString(exec, buf.constData());
267 }
268
269 JSValue* QtInstance::numberValue(ExecState* exec) const
270 {
271     return jsNumber(exec, 0);
272 }
273
274 JSValue* QtInstance::booleanValue() const
275 {
276     // ECMA 9.2
277     return jsBoolean(true);
278 }
279
280 JSValue* QtInstance::valueOf(ExecState* exec) const
281 {
282     return stringValue(exec);
283 }
284
285 // In qt_runtime.cpp
286 JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant);
287 QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance);
288
289 const char* QtField::name() const
290 {
291     if (m_type == MetaProperty)
292         return m_property.name();
293     else if (m_type == ChildObject && m_childObject)
294         return m_childObject->objectName().toLatin1();
295     else if (m_type == DynamicProperty)
296         return m_dynamicProperty.constData();
297     return ""; // deleted child object
298 }
299
300 JSValue* QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
301 {
302     const QtInstance* instance = static_cast<const QtInstance*>(inst);
303     QObject* obj = instance->getObject();
304
305     if (obj) {
306         QVariant val;
307         if (m_type == MetaProperty) {
308             if (m_property.isReadable())
309                 val = m_property.read(obj);
310             else
311                 return jsUndefined();
312         } else if (m_type == ChildObject)
313             val = QVariant::fromValue((QObject*) m_childObject);
314         else if (m_type == DynamicProperty)
315             val = obj->property(m_dynamicProperty);
316
317         JSValue* ret = convertQVariantToValue(exec, inst->rootObject(), val);
318
319         // Need to save children so we can mark them
320         if (m_type == ChildObject)
321             instance->m_children.insert(ret);
322
323         return ret;
324     } else {
325         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
326         return throwError(exec, GeneralError, msg.toLatin1().constData());
327     }
328 }
329
330 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue* aValue) const
331 {
332     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
333         return;
334
335     const QtInstance* instance = static_cast<const QtInstance*>(inst);
336     QObject* obj = instance->getObject();
337     if (obj) {
338         QMetaType::Type argtype = QMetaType::Void;
339         if (m_type == MetaProperty)
340             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
341
342         // dynamic properties just get any QVariant
343         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
344         if (m_type == MetaProperty) {
345             if (m_property.isWritable())
346                 m_property.write(obj, val);
347         } else if (m_type == DynamicProperty)
348             obj->setProperty(m_dynamicProperty.constData(), val);
349     } else {
350         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
351         throwError(exec, GeneralError, msg.toLatin1().constData());
352     }
353 }
354
355
356 }
357 }