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