KParts

scriptableextension.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 2010 Maksim Orlovich <[email protected]>
4  SPDX-FileCopyrightText: 2002, 2004 Koos Vriezen <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "scriptableextension.h"
10 #include "scriptableextension_p.h"
11 
12 namespace KParts
13 {
14 struct ScriptableExtensionPrivate {
15  ScriptableExtension *hostContext = nullptr;
16 };
17 
18 ScriptableExtension::ScriptableExtension(QObject *parent)
19  : QObject(parent)
20  , d(new ScriptableExtensionPrivate)
21 {
22 }
23 
24 ScriptableExtension::~ScriptableExtension() = default;
25 
27 {
29 }
30 
32 {
33  return new ScriptableLiveConnectExtension(parentObj, oldApi);
34 }
35 
37 {
38  d->hostContext = host;
39 }
40 
42 {
43  return d->hostContext;
44 }
45 
47 {
48  return QVariant::fromValue(Null());
49 }
50 
52 {
53  if (d->hostContext) {
54  return d->hostContext->encloserForKid(this);
55  } else {
56  return QVariant::fromValue(Null());
57  }
58 }
59 
60 QVariant ScriptableExtension::encloserForKid(KParts::ScriptableExtension *kid)
61 {
62  Q_UNUSED(kid);
63  return QVariant::fromValue(Null());
64 }
65 
66 static QVariant unimplemented()
67 {
68  ScriptableExtension::Exception except(QStringLiteral("[unimplemented]"));
69  return QVariant::fromValue(except);
70 }
71 
72 QVariant ScriptableExtension::callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
73 {
74  Q_UNUSED(callerPrincipal);
75  Q_UNUSED(objId);
76  Q_UNUSED(args);
77  return unimplemented();
78 }
79 
80 QVariant ScriptableExtension::callFunctionReference(ScriptableExtension *callerPrincipal, quint64 objId, const QString &f, const ArgList &args)
81 {
82  Q_UNUSED(callerPrincipal);
83  Q_UNUSED(objId);
84  Q_UNUSED(args);
85  Q_UNUSED(f);
86  return unimplemented();
87 }
88 
89 QVariant ScriptableExtension::callAsConstructor(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
90 {
91  Q_UNUSED(callerPrincipal);
92  Q_UNUSED(objId);
93  Q_UNUSED(args);
94  return unimplemented();
95 }
96 
97 bool ScriptableExtension::hasProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
98 {
99  Q_UNUSED(callerPrincipal);
100  Q_UNUSED(objId);
101  Q_UNUSED(propName);
102  return false;
103 }
104 
105 QVariant ScriptableExtension::get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
106 {
107  Q_UNUSED(callerPrincipal);
108  Q_UNUSED(objId);
109  Q_UNUSED(propName);
110  return unimplemented();
111 }
112 
113 bool ScriptableExtension::put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value)
114 {
115  Q_UNUSED(callerPrincipal);
116  Q_UNUSED(objId);
117  Q_UNUSED(propName);
118  Q_UNUSED(value);
119  return false;
120 }
121 
122 bool ScriptableExtension::removeProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
123 {
124  Q_UNUSED(callerPrincipal);
125  Q_UNUSED(objId);
126  Q_UNUSED(propName);
127  return false;
128 }
129 
130 bool ScriptableExtension::enumerateProperties(ScriptableExtension *callerPrincipal, quint64 objId, QStringList *result)
131 {
132  Q_UNUSED(callerPrincipal);
133  Q_UNUSED(objId);
134  Q_UNUSED(result);
135  return false;
136 }
137 
138 bool ScriptableExtension::setException(ScriptableExtension *callerPrincipal, const QString &message)
139 {
140  Q_UNUSED(callerPrincipal);
141  Q_UNUSED(message);
142  return false;
143 }
144 
145 QVariant ScriptableExtension::evaluateScript(ScriptableExtension *callerPrincipal, quint64 contextObjectId, const QString &code, ScriptLanguage language)
146 {
147  Q_UNUSED(callerPrincipal);
148  Q_UNUSED(contextObjectId);
149  Q_UNUSED(code);
150  Q_UNUSED(language);
151  return unimplemented();
152 }
153 
155 {
156  Q_UNUSED(lang);
157  return false;
158 }
159 
160 void ScriptableExtension::acquire(quint64 objId)
161 {
162  Q_UNUSED(objId);
163 }
164 
166 {
167  if (v.canConvert<Object>()) {
168  Object o = v.value<Object>();
169  o.owner->acquire(o.objId);
170  } else if (v.canConvert<FunctionRef>()) {
171  FunctionRef fr = v.value<FunctionRef>();
172  fr.base.owner->acquire(fr.base.objId);
173  }
174  return v;
175 }
176 
177 void ScriptableExtension::release(quint64 objId)
178 {
179  Q_UNUSED(objId);
180 }
181 
183 {
184  if (v.canConvert<Object>()) {
185  Object o = v.value<Object>();
186  o.owner->release(o.objId);
187  } else if (v.canConvert<FunctionRef>()) {
188  FunctionRef fr = v.value<FunctionRef>();
189  fr.base.owner->release(fr.base.objId);
190  }
191  return v;
192 }
193 
194 // LiveConnectExtension -> ScriptableExtension adapter. We use
195 // lc object IDs as our own object IDs.
196 // ----------------------------------------------------------------------------
197 ScriptableLiveConnectExtension::ScriptableLiveConnectExtension(QObject *p, LiveConnectExtension *old)
199  , wrapee(old)
200 {
201  connect(wrapee, &LiveConnectExtension::partEvent, this, &ScriptableLiveConnectExtension::liveConnectEvent);
202 }
203 
204 QVariant ScriptableLiveConnectExtension::rootObject()
205 {
206  // Plugin root is always LC object #0.
207  return acquireValue(QVariant::fromValue(ScriptableExtension::Object(this, 0)));
208 }
209 
210 bool ScriptableLiveConnectExtension::hasProperty(ScriptableExtension *, quint64 objId, const QString &propName)
211 {
212  QVariant val = get(nullptr, objId, propName);
213  bool ok = !val.canConvert<ScriptableExtension::Exception>();
214  releaseValue(val);
215  return ok;
216 }
217 
218 // Note that since we wrap around a plugin, and do not implement the browser,
219 // we do not perform XSS checks ourselves.
220 QVariant ScriptableLiveConnectExtension::callFunctionReference(ScriptableExtension *, quint64 o, const QString &f, const ScriptableExtension::ArgList &a)
221 {
222  QStringList qargs;
223  // Convert args to strings for LC use.
224  qargs.reserve(a.size());
225  for (const auto &arg : a) {
226  bool ok;
227  qargs.append(toLC(arg, &ok));
228  if (!ok) {
229  return unimplemented();
230  }
231  }
232 
233  LiveConnectExtension::Type retType;
234  unsigned long retObjId;
235  QString retVal;
236  if (wrapee->call((unsigned long)o, f, qargs, retType, retObjId, retVal)) {
237  return acquireValue(fromLC(QString(), retType, retObjId, retVal));
238  } else {
239  return unimplemented();
240  }
241 }
242 
243 QVariant ScriptableLiveConnectExtension::get(ScriptableExtension *, quint64 objId, const QString &propName)
244 {
245  LiveConnectExtension::Type retType;
246  unsigned long retObjId;
247  QString retVal;
248  if (wrapee->get((unsigned long)objId, propName, retType, retObjId, retVal)) {
249  return acquireValue(fromLC(propName, retType, retObjId, retVal));
250  } else {
251  // exception signals failure. ### inellegant
252  return unimplemented();
253  }
254 }
255 
256 bool ScriptableLiveConnectExtension::put(ScriptableExtension *, quint64 objId, const QString &propName, const QVariant &value)
257 {
258  bool ok;
259  QString val = toLC(value, &ok);
260  if (!ok) {
261  return false;
262  }
263 
264  return wrapee->put((unsigned long)objId, propName, val);
265 }
266 
267 QVariant ScriptableLiveConnectExtension::fromLC(const QString &name, LiveConnectExtension::Type type, unsigned long objId, const QString &value)
268 {
269  switch (type) {
270  case KParts::LiveConnectExtension::TypeBool: {
271  bool ok;
272  int i = value.toInt(&ok);
273  if (ok) {
274  return QVariant(bool(i));
275  }
276  return QVariant(value.toLower() == QLatin1String("true"));
277  }
278  case KParts::LiveConnectExtension::TypeObject:
279  case KParts::LiveConnectExtension::TypeFunction: {
280  if (!refCounts.contains(objId)) {
281  refCounts[objId] = 0;
282  }
283 
284  Object o = ScriptableExtension::Object(this, objId);
285  if (type == KParts::LiveConnectExtension::TypeObject) {
286  return QVariant::fromValue(o);
287  } else {
288  return QVariant::fromValue(FunctionRef(o, name));
289  }
290  }
291 
292  case KParts::LiveConnectExtension::TypeNumber:
293  return QVariant(value.toDouble());
294 
295  case KParts::LiveConnectExtension::TypeString:
296  return QVariant(value);
297 
298  case KParts::LiveConnectExtension::TypeVoid:
299  default:
300  return QVariant::fromValue(ScriptableExtension::Undefined());
301  }
302 }
303 
304 QString ScriptableLiveConnectExtension::toLC(const QVariant &in, bool *ok)
305 {
306  *ok = true; // most of the time.
307 
308  // Objects (or exceptions) can't be converted
309  if (in.canConvert<ScriptableExtension::Object>() || in.canConvert<ScriptableExtension::Exception>() || in.canConvert<ScriptableExtension::FunctionRef>()) {
310  *ok = false;
311  return QString();
312  }
313 
314  // Convert null and undefined to appropriate strings
315  // ### this matches old KHTML behavior, but is this sensible?
316  if (in.canConvert<ScriptableExtension::Null>()) {
317  return QStringLiteral("null");
318  }
319 
320  if (in.canConvert<ScriptableExtension::Undefined>()) {
321  return QStringLiteral("undefined");
322  }
323 
324  if (in.type() == QVariant::Bool) {
325  return in.toBool() ? QStringLiteral("true") : QStringLiteral("false");
326  }
327 
328  // Just stringify everything else, makes sense for nums as well.
329  if (in.canConvert<QString>()) {
330  return in.toString();
331  }
332 
333  // something really icky...
334  *ok = false;
335  return QString();
336 }
337 
338 void ScriptableLiveConnectExtension::acquire(quint64 objId)
339 {
340  ++refCounts[objId];
341 }
342 
343 void ScriptableLiveConnectExtension::release(quint64 objId)
344 {
345  int newRC = --refCounts[objId];
346  if (!newRC) {
347  if (objId != 0) {
348  wrapee->unregister((unsigned long)objId);
349  }
350  refCounts.remove(objId);
351  }
352 }
353 
354 void ScriptableLiveConnectExtension::liveConnectEvent(const unsigned long, const QString &event, const LiveConnectExtension::ArgList &args)
355 {
356  // We want to evaluate in the enclosure's context.
357  QVariant enclosure = enclosingObject();
358  if (!enclosure.canConvert<Object>()) {
359  releaseValue(enclosure);
360  // qDebug() << "No enclosure, can't evaluate";
361  return;
362  }
363 
364  Object enclosureObj = enclosure.value<Object>();
365 
366  if (!host()->isScriptLanguageSupported(ECMAScript)) {
367  releaseValue(enclosure);
368  // qDebug() << "Host can't evaluate ECMAScript";
369  }
370 
371  // Compute a string to evaluate. We need to escape a lot of stuff
372  // since we're composing a bunch of strings into one.
373  QString script = event + QLatin1Char('(');
374 
375  LiveConnectExtension::ArgList::const_iterator i = args.begin();
376  const LiveConnectExtension::ArgList::const_iterator argsBegin = i;
377  const LiveConnectExtension::ArgList::const_iterator argsEnd = args.end();
378 
379  for (; i != argsEnd; ++i) {
380  if (i != argsBegin) {
381  script += QLatin1Char(',');
382  }
383  if ((*i).first == KParts::LiveConnectExtension::TypeString) {
384  script += QLatin1Char('"') + QString((*i).second).replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char('"'), QLatin1String("\\\""))
385  + QLatin1Char('"');
386  } else {
387  script += (*i).second;
388  }
389  }
390  script += QLatin1Char(')');
391 
392  // qDebug() << script;
393 
394  // Ask host to evaluate.. (unfortunately, we can't do anything with the result,
395  // but anything that uses this interface isn't expective one in the first place)
396  QVariant result = host()->evaluateScript(this, enclosureObj.objId, script);
397 
398  releaseValue(result);
399  releaseValue(enclosure);
400 }
401 
402 // hash functions
403 // ----------------------------------------------------------------------------
404 
405 unsigned int qHash(const KParts::ScriptableExtension::Object &o, uint seed)
406 {
407  return qHash(qMakePair(o.owner, o.objId), seed);
408 }
409 
410 unsigned int qHash(const KParts::ScriptableExtension::FunctionRef &f)
411 {
412  return qHash(qMakePair(f.base, f.field));
413 }
414 
415 } // namespace KParts
416 
417 #include "moc_scriptableextension.cpp"
418 #include "moc_scriptableextension_p.cpp"
void append(const T &value)
QVariant enclosingObject()
Returns an object that represents the host()'s view of us.
void partEvent(const unsigned long objid, const QString &event, const KParts::LiveConnectExtension::ArgList &args)
notify a event from the part of object objid
QVariant fromValue(const T &value)
virtual void release(quint64 objid)
decreases reference count of object objId
virtual void acquire(quint64 objid)
increases reference count of object objId
T value() const const
An extension class that permits KParts to be scripted (such as when embedded inside a KHTMLPart) and ...
virtual bool setException(ScriptableExtension *callerPrincipal, const QString &message)
Tries to raise an exception with given message in this extension's scripting context.
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
void setHost(ScriptableExtension *host)
Reports the hosting ScriptableExtension to a child.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static QVariant releaseValue(const QVariant &v)
Helper that calls release on any object or function reference base stored in v.
Function references are a pair of an object and a field in it.
void reserve(int alloc)
An extension class for LiveConnect, i.e. a call from JavaScript from a HTML page which embeds this pa...
QVariant::Type type() const const
virtual QVariant callAsConstructor(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
Try to use the object objId associated with 'this' as a constructor (corresponding to ECMAScript's ne...
FindDirectChildrenOnly
virtual bool enumerateProperties(ScriptableExtension *callerPrincipal, quint64 objId, QStringList *result)
Tries to enumerate all fields of object objId associated with this to result.
virtual bool isScriptLanguageSupported(ScriptLanguage lang) const
returns true if this extension can execute scripts in the given language
int toInt(bool *ok, int base) const const
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
virtual QVariant rootObject()
Return the root scriptable object of this KPart.
Objects are abstracted away as a pair of the ScriptableExtension the performs operations on it,...
double toDouble(bool *ok) const const
bool toBool() const const
QString & replace(int position, int n, QChar after)
T findChild(const QString &name, Qt::FindChildOptions options) const const
bool canConvert(int targetTypeId) const const
static ScriptableExtension * childObject(QObject *obj)
Queries obj for a child object which inherits from this ScriptableExtension class.
ScriptableExtension * host() const
Returns any registered parent scripting context.
QString toLower() const const
Corresponds to 'null' in JavaScript.
static QVariant acquireValue(const QVariant &v)
Helper that calls acquire on any object or function reference base stored in v.
virtual QVariant callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
Try to use the object objId associated with 'this' as a function.
virtual bool hasProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
Returns true if the object objId associated with 'this' has the property propName.
virtual QVariant callFunctionReference(ScriptableExtension *callerPrincipal, quint64 objId, const QString &f, const ArgList &args)
Try to use a function reference to field f of object @objId as a function.
virtual QVariant evaluateScript(ScriptableExtension *callerPrincipal, quint64 contextObjectId, const QString &code, ScriptLanguage language=ECMAScript)
Tries to evaluate a script code with the given object as its context.
virtual bool put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value)
Tries to set the field propName from object objId associated with 'this' to value.
virtual bool removeProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
Tries to remove the field d propName from object objId associated with 'this'.
static ScriptableExtension * adapterFromLiveConnect(QObject *parentObj, LiveConnectExtension *oldApi)
This returns a bridge object that permits KParts implementing the older LiveConnectExtension to be us...
The KParts namespace,.
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
Tries to get field propName from object objId associated with 'this'.
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Tue Aug 16 2022 03:47:23 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.