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