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