1/* This file is part of the KDE project
2 Copyright (C) 2010 Maksim Orlovich <maksim@kde.org>
3 Copyright (C) 2002, 2004 Koos Vriezen <koos.vriezen@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20#include "scriptableextension.h"
21#include "scriptableextension_p.h"
22#include <kglobal.h>
23#include <kdebug.h>
24
25namespace KParts {
26
27struct ScriptableExtensionPrivate {
28 ScriptableExtension* hostContext;
29
30 ScriptableExtensionPrivate(): hostContext(0)
31 {}
32};
33
34ScriptableExtension::ScriptableExtension(QObject* parent):
35 QObject(parent), d(new ScriptableExtensionPrivate)
36{}
37
38ScriptableExtension::~ScriptableExtension()
39{
40 delete d;
41}
42
43ScriptableExtension* ScriptableExtension::childObject(QObject* obj)
44{
45 return KGlobal::findDirectChild<KParts::ScriptableExtension*>(obj);
46}
47
48ScriptableExtension* ScriptableExtension::adapterFromLiveConnect(QObject* parentObj,
49 LiveConnectExtension* oldApi)
50{
51 return new ScriptableLiveConnectExtension(parentObj, oldApi);
52}
53
54void ScriptableExtension::setHost(ScriptableExtension* host)
55{
56 d->hostContext = host;
57}
58
59ScriptableExtension* ScriptableExtension::host() const
60{
61 return d->hostContext;
62}
63
64QVariant ScriptableExtension::rootObject()
65{
66 return QVariant::fromValue(Null());
67}
68
69QVariant ScriptableExtension::enclosingObject()
70{
71 if (d->hostContext)
72 return d->hostContext->encloserForKid(this);
73 else
74 return QVariant::fromValue(Null());
75}
76
77QVariant ScriptableExtension::encloserForKid(KParts::ScriptableExtension* kid)
78{
79 Q_UNUSED(kid);
80 return QVariant::fromValue(Null());
81}
82
83static QVariant unimplemented()
84{
85 ScriptableExtension::Exception except(QString::fromLatin1("[unimplemented]"));
86 return QVariant::fromValue(except);
87}
88
89QVariant ScriptableExtension::callAsFunction(ScriptableExtension* callerPrincipal,
90 quint64 objId, const ArgList& args)
91{
92 Q_UNUSED(callerPrincipal);
93 Q_UNUSED(objId);
94 Q_UNUSED(args);
95 return unimplemented();
96}
97
98QVariant ScriptableExtension::callFunctionReference(ScriptableExtension* callerPrincipal,
99 quint64 objId, const QString& f,
100 const ArgList& args)
101{
102 Q_UNUSED(callerPrincipal);
103 Q_UNUSED(objId);
104 Q_UNUSED(args);
105 Q_UNUSED(f);
106 return unimplemented();
107}
108
109QVariant ScriptableExtension::callAsConstructor(ScriptableExtension* callerPrincipal,
110 quint64 objId, const ArgList& args)
111{
112 Q_UNUSED(callerPrincipal);
113 Q_UNUSED(objId);
114 Q_UNUSED(args);
115 return unimplemented();
116}
117
118bool ScriptableExtension::hasProperty(ScriptableExtension* callerPrincipal,
119 quint64 objId, const QString& propName)
120{
121 Q_UNUSED(callerPrincipal);
122 Q_UNUSED(objId);
123 Q_UNUSED(propName);
124 return false;
125}
126
127QVariant ScriptableExtension::get(ScriptableExtension* callerPrincipal,
128 quint64 objId, const QString& propName)
129{
130 Q_UNUSED(callerPrincipal);
131 Q_UNUSED(objId);
132 Q_UNUSED(propName);
133 return unimplemented();
134}
135
136bool ScriptableExtension::put(ScriptableExtension* callerPrincipal, quint64 objId,
137 const QString& propName, const QVariant& value)
138{
139 Q_UNUSED(callerPrincipal);
140 Q_UNUSED(objId);
141 Q_UNUSED(propName);
142 Q_UNUSED(value);
143 return false;
144}
145
146bool ScriptableExtension::removeProperty(ScriptableExtension* callerPrincipal,
147 quint64 objId, const QString& propName)
148{
149 Q_UNUSED(callerPrincipal);
150 Q_UNUSED(objId);
151 Q_UNUSED(propName);
152 return false;
153}
154
155bool ScriptableExtension::enumerateProperties(ScriptableExtension* callerPrincipal,
156 quint64 objId, QStringList* result)
157{
158 Q_UNUSED(callerPrincipal);
159 Q_UNUSED(objId);
160 Q_UNUSED(result);
161 return false;
162}
163
164bool ScriptableExtension::setException(ScriptableExtension* callerPrincipal,
165 const QString& message)
166{
167 Q_UNUSED(callerPrincipal);
168 Q_UNUSED(message);
169 return false;
170}
171
172QVariant ScriptableExtension::evaluateScript(ScriptableExtension* callerPrincipal,
173 quint64 contextObjectId,
174 const QString& code,
175 ScriptLanguage language)
176{
177 Q_UNUSED(callerPrincipal);
178 Q_UNUSED(contextObjectId);
179 Q_UNUSED(code);
180 Q_UNUSED(language);
181 return unimplemented();
182}
183
184bool ScriptableExtension::isScriptLanguageSupported(ScriptLanguage lang) const
185{
186 Q_UNUSED(lang);
187 return false;
188}
189
190void ScriptableExtension::acquire(quint64 objId)
191{
192 Q_UNUSED(objId);
193}
194
195QVariant ScriptableExtension::acquireValue(const QVariant& v)
196{
197 if (v.canConvert<Object>()) {
198 Object o = v.value<Object>();
199 o.owner->acquire(o.objId);
200 } else if (v.canConvert<FunctionRef>()) {
201 FunctionRef fr = v.value<FunctionRef>();
202 fr.base.owner->acquire(fr.base.objId);
203 }
204 return v;
205}
206
207void ScriptableExtension::release(quint64 objId)
208{
209 Q_UNUSED(objId);
210}
211
212QVariant ScriptableExtension::releaseValue(const QVariant& v)
213{
214 if (v.canConvert<Object>()) {
215 Object o = v.value<Object>();
216 o.owner->release(o.objId);
217 } else if (v.canConvert<FunctionRef>()) {
218 FunctionRef fr = v.value<FunctionRef>();
219 fr.base.owner->release(fr.base.objId);
220 }
221 return v;
222}
223
224// LiveConnectExtension -> ScriptableExtension adapter. We use
225// lc object IDs as our own object IDs.
226// ----------------------------------------------------------------------------
227ScriptableLiveConnectExtension::ScriptableLiveConnectExtension(QObject* p, LiveConnectExtension* old):
228 ScriptableExtension(p), wrapee(old)
229{
230 connect(wrapee,
231 SIGNAL(partEvent(ulong,QString,KParts::LiveConnectExtension::ArgList)),
232 this,
233 SLOT(liveConnectEvent(ulong,QString,KParts::LiveConnectExtension::ArgList)));
234}
235
236QVariant ScriptableLiveConnectExtension::rootObject()
237{
238 // Plugin root is always LC object #0.
239 return acquireValue(QVariant::fromValue(ScriptableExtension::Object(this, 0)));
240}
241
242bool ScriptableLiveConnectExtension::hasProperty(ScriptableExtension*, quint64 objId, const QString& propName)
243{
244 QVariant val = get(0, objId, propName);
245 bool ok = !val.canConvert<ScriptableExtension::Exception>();
246 releaseValue(val);
247 return ok;
248}
249
250// Note that since we wrap around a plugin, and do not implement the browser,
251// we do not perform XSS checks ourselves.
252QVariant ScriptableLiveConnectExtension::callFunctionReference(ScriptableExtension*,
253 quint64 o, const QString& f, const ScriptableExtension::ArgList& a)
254{
255 QStringList qargs;
256 // Convert args to strings for LC use.
257 for (int i = 0; i < a.size(); ++i) {
258 bool ok;
259 qargs.append(toLC(a[i], &ok));
260 if (!ok)
261 return unimplemented();
262 }
263
264 LiveConnectExtension::Type retType;
265 unsigned long retObjId;
266 QString retVal;
267 if (wrapee->call((unsigned long)o, f, qargs, retType, retObjId, retVal)) {
268 return acquireValue(fromLC(QString(), retType, retObjId, retVal));
269 } else {
270 return unimplemented();
271 }
272}
273
274QVariant ScriptableLiveConnectExtension::get(ScriptableExtension*,
275 quint64 objId, const QString& propName)
276{
277 LiveConnectExtension::Type retType;
278 unsigned long retObjId;
279 QString retVal;
280 if (wrapee->get((unsigned long)objId, propName, retType, retObjId, retVal)) {
281 return acquireValue(fromLC(propName, retType, retObjId, retVal));
282 } else {
283 // exception signals failure. ### inellegant
284 return unimplemented();
285 }
286}
287
288bool ScriptableLiveConnectExtension::put(ScriptableExtension*, quint64 objId,
289 const QString& propName, const QVariant& value)
290{
291 bool ok;
292 QString val = toLC(value, &ok);
293 if (!ok)
294 return false;
295
296 return wrapee->put((unsigned long)objId, propName, val);
297}
298
299QVariant ScriptableLiveConnectExtension::fromLC(const QString& name,
300 LiveConnectExtension::Type type,
301 unsigned long objId,
302 const QString& value)
303{
304 switch (type) {
305 case KParts::LiveConnectExtension::TypeBool: {
306 bool ok;
307 int i = value.toInt(&ok);
308 if (ok)
309 return QVariant(bool(i));
310 return QVariant(value.toLower() == QLatin1String("true"));
311 }
312 case KParts::LiveConnectExtension::TypeObject:
313 case KParts::LiveConnectExtension::TypeFunction: {
314 if (!refCounts.contains(objId))
315 refCounts[objId] = 0;
316
317 Object o = ScriptableExtension::Object(this, objId);
318 if (type == KParts::LiveConnectExtension::TypeObject)
319 return QVariant::fromValue(o);
320 else
321 return QVariant::fromValue(FunctionRef(o, name));
322 }
323
324 case KParts::LiveConnectExtension::TypeNumber:
325 return QVariant(value.toDouble());
326
327 case KParts::LiveConnectExtension::TypeString:
328 return QVariant(value);
329
330 case KParts::LiveConnectExtension::TypeVoid:
331 default:
332 return QVariant::fromValue(ScriptableExtension::Undefined());
333 }
334}
335
336QString ScriptableLiveConnectExtension::toLC(const QVariant& in, bool* ok)
337{
338 *ok = true; // most of the time.
339
340 // Objects (or exceptions) can't be converted
341 if (in.canConvert<ScriptableExtension::Object>() ||
342 in.canConvert<ScriptableExtension::Exception>() ||
343 in.canConvert<ScriptableExtension::FunctionRef>()) {
344
345 *ok = false;
346 return QString();
347 }
348
349 // Convert null and undefined to appropriate strings
350 // ### this matches old KHTML behavior, but is this sensible?
351 if (in.canConvert<ScriptableExtension::Null>())
352 return QString::fromLatin1("null");
353
354 if (in.canConvert<ScriptableExtension::Undefined>())
355 return QString::fromLatin1("undefined");
356
357 if (in.type() == QVariant::Bool)
358 return in.toBool() ? QString::fromLatin1("true") : QString::fromLatin1("false");
359
360 // Just stringify everything else, makes sense for nums as well.
361 if (in.canConvert<QString>())
362 return in.toString();
363
364 // something really icky...
365 *ok = false;
366 return QString();
367}
368
369void ScriptableLiveConnectExtension::acquire(quint64 objId)
370{
371 ++refCounts[objId];
372}
373
374void ScriptableLiveConnectExtension::release(quint64 objId)
375{
376 int newRC = --refCounts[objId];
377 if (!newRC) {
378 if (objId != 0)
379 wrapee->unregister((unsigned long)objId);
380 refCounts.remove(objId);
381 }
382}
383
384void ScriptableLiveConnectExtension::liveConnectEvent(const unsigned long, const QString& event,
385 const LiveConnectExtension::ArgList& args)
386{
387 // We want to evaluate in the enclosure's context.
388 QVariant enclosure = enclosingObject();
389 if (!enclosure.canConvert<Object>()) {
390 releaseValue(enclosure);
391 kDebug(1000) << "No enclosure, can't evaluate";
392 return;
393 }
394
395 Object enclosureObj = enclosure.value<Object>();
396
397 if (!host()->isScriptLanguageSupported(ECMAScript)) {
398 releaseValue(enclosure);
399 kDebug(1000) << "Host can't evaluate ECMAScript";
400 }
401
402 // Compute a string to evaluate. We ned to escape a lot of stuff
403 // since we're composing a bunch of strings into one.
404 QString script;
405 script.sprintf("%s(", event.toLatin1().constData());
406
407 LiveConnectExtension::ArgList::const_iterator i = args.begin();
408 const LiveConnectExtension::ArgList::const_iterator argsBegin = i;
409 const LiveConnectExtension::ArgList::const_iterator argsEnd = args.end();
410
411 for ( ; i != argsEnd; ++i) {
412 if (i != argsBegin)
413 script += ",";
414 if ((*i).first == KParts::LiveConnectExtension::TypeString) {
415 script += "\"";
416 script += QString((*i).second).replace('\\', "\\\\").replace('"', "\\\"");
417 script += "\"";
418 } else
419 script += (*i).second;
420 }
421 script += ")";
422
423 kDebug(1000) << script;
424
425 // Ask host to evaluate.. (unfortunately, we can't do anything with the result,
426 // but anything that uses this interface isn't expective one in the first place)
427 QVariant result = host()->evaluateScript(this, enclosureObj.objId, script);
428
429 releaseValue(result);
430 releaseValue(enclosure);
431}
432
433// hash functions
434// ----------------------------------------------------------------------------
435
436unsigned int qHash(const KParts::ScriptableExtension::Object& o)
437{
438 return qHash(qMakePair(o.owner, o.objId));
439}
440
441unsigned int qHash(const KParts::ScriptableExtension::FunctionRef& f)
442{
443 return qHash(qMakePair(f.base, f.field));
444}
445
446} // namespace KParts
447
448
449
450#include "scriptableextension.moc"
451#include "scriptableextension_p.moc"
452// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
453
454