26fd7011fbb2803a31092a19fff52553ece5ad6f
[WebKit-https.git] / 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 "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 // Derived RuntimeObject
47 class QtRuntimeObjectImp : public RuntimeObjectImp {
48 public:
49     QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
50
51     static const ClassInfo s_info;
52
53     virtual void markChildren(MarkStack& markStack)
54     {
55         RuntimeObjectImp::markChildren(markStack);
56         QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
57         if (instance)
58             instance->markAggregate(markStack);
59     }
60
61     static PassRefPtr<Structure> createStructure(JSValue prototype)
62     {
63         return Structure::create(prototype, TypeInfo(ObjectType,  StructureFlags));
64     }
65
66 protected:
67     static const unsigned StructureFlags = RuntimeObjectImp::StructureFlags | OverridesMarkChildren;
68
69 private:
70     virtual const ClassInfo* classInfo() const { return &s_info; }
71 };
72
73 const ClassInfo QtRuntimeObjectImp::s_info = { "QtRuntimeObject", &RuntimeObjectImp::s_info, 0, 0 };
74
75 QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
76     : RuntimeObjectImp(exec, WebCore::deprecatedGetDOMStructure<QtRuntimeObjectImp>(exec), instance)
77 {
78 }
79
80 // QtInstance
81 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
82     : Instance(rootObject)
83     , m_class(0)
84     , m_object(o)
85     , m_hashkey(o)
86     , m_defaultMethod(0)
87     , m_ownership(ownership)
88 {
89 }
90
91 QtInstance::~QtInstance()
92 {
93     JSLock lock(SilenceAssertionsOnly);
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 QScriptEngine::QtOwnership:
106             break;
107         case QScriptEngine::AutoOwnership:
108             if (m_object->parent())
109                 break;
110             // fall through!
111         case QScriptEngine::ScriptOwnership:
112             delete m_object;
113             break;
114         }
115     }
116 }
117
118 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
119 {
120     JSLock lock(SilenceAssertionsOnly);
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, ownership);
128     cachedInstances.insert(o, ret.get());
129
130     return ret.release();
131 }
132
133 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
134 {
135     return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
136 }
137
138 void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
139 {
140     object->JSObject::put(exec, propertyName, value, slot);
141 }
142
143 void QtInstance::removeCachedMethod(JSObject* method)
144 {
145     if (m_defaultMethod == method)
146         m_defaultMethod = 0;
147
148     for(QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(),
149         end = m_methods.end(); it != end; ++it)
150         if (it.value() == method) {
151             m_methods.erase(it);
152             return;
153         }
154 }
155
156 QtInstance* QtInstance::getInstance(JSObject* object)
157 {
158     if (!object)
159         return 0;
160     if (!object->inherits(&QtRuntimeObjectImp::s_info))
161         return 0;
162     return static_cast<QtInstance*>(static_cast<RuntimeObjectImp*>(object)->getInternalInstance());
163 }
164
165 Class* QtInstance::getClass() const
166 {
167     if (!m_class)
168         m_class = QtClass::classForObject(m_object);
169     return m_class;
170 }
171
172 RuntimeObjectImp* QtInstance::newRuntimeObject(ExecState* exec)
173 {
174     JSLock lock(SilenceAssertionsOnly);
175     return new (exec) QtRuntimeObjectImp(exec, this);
176 }
177
178 void QtInstance::markAggregate(MarkStack& markStack)
179 {
180     if (m_defaultMethod)
181         markStack.append(m_defaultMethod);
182     foreach(JSObject* val, m_methods.values()) {
183         if (val)
184             markStack.append(val);
185     }
186 }
187
188 void QtInstance::begin()
189 {
190     // Do nothing.
191 }
192
193 void QtInstance::end()
194 {
195     // Do nothing.
196 }
197
198 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
199 {
200     // This is the enumerable properties, so put:
201     // properties
202     // dynamic properties
203     // slots
204     QObject* obj = getObject();
205     if (obj) {
206         const QMetaObject* meta = obj->metaObject();
207
208         int i;
209         for (i=0; i < meta->propertyCount(); i++) {
210             QMetaProperty prop = meta->property(i);
211             if (prop.isScriptable()) {
212                 array.add(Identifier(exec, prop.name()));
213             }
214         }
215
216         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
217         foreach(QByteArray ba, dynProps) {
218             array.add(Identifier(exec, ba.constData()));
219         }
220
221         for (i=0; i < meta->methodCount(); i++) {
222             QMetaMethod method = meta->method(i);
223             if (method.access() != QMetaMethod::Private) {
224                 array.add(Identifier(exec, method.signature()));
225             }
226         }
227     }
228 }
229
230 JSValue QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
231 {
232     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
233     return jsUndefined();
234 }
235
236
237 JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
238 {
239     if (hint == PreferString)
240         return stringValue(exec);
241     if (hint == PreferNumber)
242         return numberValue(exec);
243     return valueOf(exec);
244 }
245
246 JSValue QtInstance::stringValue(ExecState* exec) const
247 {
248     // Hmm.. see if there is a toString defined
249     QByteArray buf;
250     bool useDefault = true;
251     getClass();
252     QObject* obj = getObject();
253     if (m_class && obj) {
254         // Cheat and don't use the full name resolution
255         int index = obj->metaObject()->indexOfMethod("toString()");
256         if (index >= 0) {
257             QMetaMethod m = obj->metaObject()->method(index);
258             // Check to see how much we can call it
259             if (m.access() != QMetaMethod::Private
260                 && m.methodType() != QMetaMethod::Signal
261                 && m.parameterTypes().count() == 0) {
262                 const char* retsig = m.typeName();
263                 if (retsig && *retsig) {
264                     QVariant ret(QMetaType::type(retsig), (void*)0);
265                     void * qargs[1];
266                     qargs[0] = ret.data();
267
268                     if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
269                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
270                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
271                             useDefault = false;
272                         }
273                     }
274                 }
275             }
276         }
277     }
278
279     if (useDefault) {
280         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
281         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
282         QString str = QString::fromUtf8("%0(name = \"%1\")")
283                       .arg(QLatin1String(meta->className())).arg(name);
284
285         buf = str.toLatin1();
286     }
287     return jsString(exec, buf.constData());
288 }
289
290 JSValue QtInstance::numberValue(ExecState* exec) const
291 {
292     return jsNumber(exec, 0);
293 }
294
295 JSValue QtInstance::booleanValue() const
296 {
297     // ECMA 9.2
298     return jsBoolean(true);
299 }
300
301 JSValue QtInstance::valueOf(ExecState* exec) const
302 {
303     return stringValue(exec);
304 }
305
306 // In qt_runtime.cpp
307 JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
308 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
309
310 const char* QtField::name() const
311 {
312     if (m_type == MetaProperty)
313         return m_property.name();
314     else if (m_type == ChildObject && m_childObject)
315         return m_childObject->objectName().toLatin1();
316     else if (m_type == DynamicProperty)
317         return m_dynamicProperty.constData();
318     return ""; // deleted child object
319 }
320
321 JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
322 {
323     const QtInstance* instance = static_cast<const QtInstance*>(inst);
324     QObject* obj = instance->getObject();
325
326     if (obj) {
327         QVariant val;
328         if (m_type == MetaProperty) {
329             if (m_property.isReadable())
330                 val = m_property.read(obj);
331             else
332                 return jsUndefined();
333         } else if (m_type == ChildObject)
334             val = QVariant::fromValue((QObject*) m_childObject);
335         else if (m_type == DynamicProperty)
336             val = obj->property(m_dynamicProperty);
337
338         return convertQVariantToValue(exec, inst->rootObject(), val);
339     } else {
340         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
341         return throwError(exec, GeneralError, msg.toLatin1().constData());
342     }
343 }
344
345 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
346 {
347     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
348         return;
349
350     const QtInstance* instance = static_cast<const QtInstance*>(inst);
351     QObject* obj = instance->getObject();
352     if (obj) {
353         QMetaType::Type argtype = QMetaType::Void;
354         if (m_type == MetaProperty)
355             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
356
357         // dynamic properties just get any QVariant
358         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
359         if (m_type == MetaProperty) {
360             if (m_property.isWritable())
361                 m_property.write(obj, val);
362         } else if (m_type == DynamicProperty)
363             obj->setProperty(m_dynamicProperty.constData(), val);
364     } else {
365         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
366         throwError(exec, GeneralError, msg.toLatin1().constData());
367     }
368 }
369
370
371 }
372 }