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