Michael Goddard <michael.goddard@trolltech.com>
[WebKit-https.git] / WebCore / bridge / qt / qt_class.cpp
1 /*
2  * Copyright (C) 2006 Trolltech ASA
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 "identifier.h"
22
23 #include "qt_class.h"
24 #include "qt_instance.h"
25 #include "qt_runtime.h"
26
27 #include <qmetaobject.h>
28 #include <qdebug.h>
29
30 namespace KJS {
31 namespace Bindings {
32
33 QtClass::QtClass(const QMetaObject* mo)
34     : m_metaObject(mo)
35 {
36 }
37
38 QtClass::~QtClass()
39 {
40 }
41
42 typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject;
43 static ClassesByMetaObject* classesByMetaObject = 0;
44
45 QtClass* QtClass::classForObject(QObject* o)
46 {
47     if (!classesByMetaObject)
48         classesByMetaObject = new ClassesByMetaObject;
49
50     const QMetaObject* mo = o->metaObject();
51     QtClass* aClass = classesByMetaObject->get(mo);
52     if (!aClass) {
53         aClass = new QtClass(mo);
54         classesByMetaObject->set(mo, aClass);
55     }
56
57     return aClass;
58 }
59
60 const char* QtClass::name() const
61 {
62     return m_metaObject->className();
63 }
64
65 // We use this to get at signals (so we can return a proper function object,
66 // and not get wrapped in RuntimeMethod).  Also, use this for methods,
67 // so we can cache the JSValue* and return the same JSValue for the same
68 // identifier...
69 JSValue* QtClass::fallbackObject(ExecState *exec, Instance *inst, const Identifier &identifier)
70 {
71     QtInstance* qtinst = static_cast<QtInstance*>(inst);
72
73     QByteArray name(identifier.ascii());
74
75     // First see if we have a cache hit
76     JSValue* val = qtinst->m_methods.value(name);
77     if (val)
78         return val;
79
80     // Nope, create an entry
81     QByteArray normal = QMetaObject::normalizedSignature(name.constData());
82
83     // See if there is an exact match
84     int index = -1;
85     if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) {
86         QMetaMethod m = m_metaObject->method(index);
87         if (m.access() != QMetaMethod::Private) {
88             JSValue *val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
89             qtinst->m_methods.insert(name, val);
90             return val;
91         }
92     }
93
94     // Nope.. try a basename match
95     int count = m_metaObject->methodCount();
96     for (index = count - 1; index >= 0; --index) {
97         const QMetaMethod m = m_metaObject->method(index);
98         if (m.access() == QMetaMethod::Private)
99             continue;
100
101         QByteArray signature = m.signature();
102         signature.truncate(signature.indexOf('('));
103
104         if (normal == signature) {
105             JSValue* val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
106             qtinst->m_methods.insert(name, val);
107             return val;
108         }
109     }
110
111     return jsUndefined();
112 }
113
114 // This functionality is handled by the fallback case above...
115 MethodList QtClass::methodsNamed(const Identifier&, Instance*) const
116 {
117     return MethodList();
118 }
119
120 // ### we may end up with a different search order than QtScript by not
121 // folding this code into the fallbackMethod above, but Fields propagate out
122 // of the binding code
123 Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const
124 {
125     // Check static properties first
126     QtInstance* qtinst = static_cast<QtInstance*>(instance);
127
128     QObject* obj = qtinst->getObject();
129     UString ustring = identifier.ustring();
130     QString objName(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
131     QByteArray ba = objName.toAscii();
132
133     // First check for a cached field
134     QtField* f = qtinst->m_fields.value(objName);
135
136     if (obj) {
137         if (f) {
138             // We only cache real metaproperties, but we do store the
139             // other types so we can delete them later
140             if (f->fieldType() == QtField::MetaProperty)
141                 return f;
142             else if (f->fieldType() == QtField::DynamicProperty) {
143                 if (obj->dynamicPropertyNames().indexOf(ba) >= 0)
144                     return f;
145                 else {
146                     // Dynamic property that disappeared
147                     qtinst->m_fields.remove(objName);
148                     delete f;
149                 }
150             } else {
151                 QList<QObject*> children = obj->children();
152                 for (int index = 0; index < children.count(); ++index) {
153                     QObject *child = children.at(index);
154                     if (child->objectName() == objName)
155                         return f;
156                 }
157
158                 // Didn't find it, delete it from the cache
159                 qtinst->m_fields.remove(objName);
160                 delete f;
161             }
162         }
163
164         int index = m_metaObject->indexOfProperty(identifier.ascii());
165         if (index >= 0) {
166             QMetaProperty prop = m_metaObject->property(index);
167
168             if (prop.isScriptable(obj)) {
169                 f = new QtField(prop);
170                 qtinst->m_fields.insert(objName, f);
171                 return f;
172             }
173         }
174
175         // Dynamic properties
176         index = obj->dynamicPropertyNames().indexOf(ba);
177         if (index >= 0) {
178             f = new QtField(ba);
179             qtinst->m_fields.insert(objName, f);
180             return f;
181         }
182
183         // Child objects
184
185         QList<QObject*> children = obj->children();
186         for (index = 0; index < children.count(); ++index) {
187             QObject *child = children.at(index);
188             if (child->objectName() == objName) {
189                 f = new QtField(child);
190                 qtinst->m_fields.insert(objName, f);
191                 return f;
192             }
193         }
194
195         // Nothing named this
196         return 0;
197     } else {
198         QByteArray ba(identifier.ascii());
199         // For compatibility with qtscript, cached methods don't cause
200         // errors until they are accessed, so don't blindly create an error
201         // here.
202         if (qtinst->m_methods.contains(ba))
203             return 0;
204
205         // deleted qobject, but can't throw an error from here (no exec)
206         // create a fake QtField that will throw upon access
207         if (!f) {
208             f = new QtField(ba);
209             qtinst->m_fields.insert(objName, f);
210         }
211         return f;
212     }
213 }
214
215 }
216 }
217