File indexing completed on 2024-04-28 15:29:24
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2010 Maksim Orlovich <maksim@kde.org> 0004 SPDX-FileCopyrightText: 2002, 2004 Koos Vriezen <koos.vriezen@gmail.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "scriptableextension.h" 0010 #include "scriptableextension_p.h" 0011 0012 namespace KParts 0013 { 0014 struct ScriptableExtensionPrivate { 0015 ScriptableExtension *hostContext = nullptr; 0016 }; 0017 0018 ScriptableExtension::ScriptableExtension(QObject *parent) 0019 : QObject(parent) 0020 , d(new ScriptableExtensionPrivate) 0021 { 0022 } 0023 0024 ScriptableExtension::~ScriptableExtension() = default; 0025 0026 ScriptableExtension *ScriptableExtension::childObject(QObject *obj) 0027 { 0028 return obj->findChild<KParts::ScriptableExtension *>(QString(), Qt::FindDirectChildrenOnly); 0029 } 0030 0031 ScriptableExtension *ScriptableExtension::adapterFromLiveConnect(QObject *parentObj, LiveConnectExtension *oldApi) 0032 { 0033 return new ScriptableLiveConnectExtension(parentObj, oldApi); 0034 } 0035 0036 void ScriptableExtension::setHost(ScriptableExtension *host) 0037 { 0038 d->hostContext = host; 0039 } 0040 0041 ScriptableExtension *ScriptableExtension::host() const 0042 { 0043 return d->hostContext; 0044 } 0045 0046 QVariant ScriptableExtension::rootObject() 0047 { 0048 return QVariant::fromValue(Null()); 0049 } 0050 0051 QVariant ScriptableExtension::enclosingObject() 0052 { 0053 if (d->hostContext) { 0054 return d->hostContext->encloserForKid(this); 0055 } else { 0056 return QVariant::fromValue(Null()); 0057 } 0058 } 0059 0060 QVariant ScriptableExtension::encloserForKid(KParts::ScriptableExtension *kid) 0061 { 0062 Q_UNUSED(kid); 0063 return QVariant::fromValue(Null()); 0064 } 0065 0066 static QVariant unimplemented() 0067 { 0068 ScriptableExtension::Exception except(QStringLiteral("[unimplemented]")); 0069 return QVariant::fromValue(except); 0070 } 0071 0072 QVariant ScriptableExtension::callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args) 0073 { 0074 Q_UNUSED(callerPrincipal); 0075 Q_UNUSED(objId); 0076 Q_UNUSED(args); 0077 return unimplemented(); 0078 } 0079 0080 QVariant ScriptableExtension::callFunctionReference(ScriptableExtension *callerPrincipal, quint64 objId, const QString &f, const ArgList &args) 0081 { 0082 Q_UNUSED(callerPrincipal); 0083 Q_UNUSED(objId); 0084 Q_UNUSED(args); 0085 Q_UNUSED(f); 0086 return unimplemented(); 0087 } 0088 0089 QVariant ScriptableExtension::callAsConstructor(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args) 0090 { 0091 Q_UNUSED(callerPrincipal); 0092 Q_UNUSED(objId); 0093 Q_UNUSED(args); 0094 return unimplemented(); 0095 } 0096 0097 bool ScriptableExtension::hasProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) 0098 { 0099 Q_UNUSED(callerPrincipal); 0100 Q_UNUSED(objId); 0101 Q_UNUSED(propName); 0102 return false; 0103 } 0104 0105 QVariant ScriptableExtension::get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) 0106 { 0107 Q_UNUSED(callerPrincipal); 0108 Q_UNUSED(objId); 0109 Q_UNUSED(propName); 0110 return unimplemented(); 0111 } 0112 0113 bool ScriptableExtension::put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value) 0114 { 0115 Q_UNUSED(callerPrincipal); 0116 Q_UNUSED(objId); 0117 Q_UNUSED(propName); 0118 Q_UNUSED(value); 0119 return false; 0120 } 0121 0122 bool ScriptableExtension::removeProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) 0123 { 0124 Q_UNUSED(callerPrincipal); 0125 Q_UNUSED(objId); 0126 Q_UNUSED(propName); 0127 return false; 0128 } 0129 0130 bool ScriptableExtension::enumerateProperties(ScriptableExtension *callerPrincipal, quint64 objId, QStringList *result) 0131 { 0132 Q_UNUSED(callerPrincipal); 0133 Q_UNUSED(objId); 0134 Q_UNUSED(result); 0135 return false; 0136 } 0137 0138 bool ScriptableExtension::setException(ScriptableExtension *callerPrincipal, const QString &message) 0139 { 0140 Q_UNUSED(callerPrincipal); 0141 Q_UNUSED(message); 0142 return false; 0143 } 0144 0145 QVariant ScriptableExtension::evaluateScript(ScriptableExtension *callerPrincipal, quint64 contextObjectId, const QString &code, ScriptLanguage language) 0146 { 0147 Q_UNUSED(callerPrincipal); 0148 Q_UNUSED(contextObjectId); 0149 Q_UNUSED(code); 0150 Q_UNUSED(language); 0151 return unimplemented(); 0152 } 0153 0154 bool ScriptableExtension::isScriptLanguageSupported(ScriptLanguage lang) const 0155 { 0156 Q_UNUSED(lang); 0157 return false; 0158 } 0159 0160 void ScriptableExtension::acquire(quint64 objId) 0161 { 0162 Q_UNUSED(objId); 0163 } 0164 0165 QVariant ScriptableExtension::acquireValue(const QVariant &v) 0166 { 0167 if (v.canConvert<Object>()) { 0168 Object o = v.value<Object>(); 0169 o.owner->acquire(o.objId); 0170 } else if (v.canConvert<FunctionRef>()) { 0171 FunctionRef fr = v.value<FunctionRef>(); 0172 fr.base.owner->acquire(fr.base.objId); 0173 } 0174 return v; 0175 } 0176 0177 void ScriptableExtension::release(quint64 objId) 0178 { 0179 Q_UNUSED(objId); 0180 } 0181 0182 QVariant ScriptableExtension::releaseValue(const QVariant &v) 0183 { 0184 if (v.canConvert<Object>()) { 0185 Object o = v.value<Object>(); 0186 o.owner->release(o.objId); 0187 } else if (v.canConvert<FunctionRef>()) { 0188 FunctionRef fr = v.value<FunctionRef>(); 0189 fr.base.owner->release(fr.base.objId); 0190 } 0191 return v; 0192 } 0193 0194 // LiveConnectExtension -> ScriptableExtension adapter. We use 0195 // lc object IDs as our own object IDs. 0196 // ---------------------------------------------------------------------------- 0197 ScriptableLiveConnectExtension::ScriptableLiveConnectExtension(QObject *p, LiveConnectExtension *old) 0198 : ScriptableExtension(p) 0199 , wrapee(old) 0200 { 0201 connect(wrapee, &LiveConnectExtension::partEvent, this, &ScriptableLiveConnectExtension::liveConnectEvent); 0202 } 0203 0204 QVariant ScriptableLiveConnectExtension::rootObject() 0205 { 0206 // Plugin root is always LC object #0. 0207 return acquireValue(QVariant::fromValue(ScriptableExtension::Object(this, 0))); 0208 } 0209 0210 bool ScriptableLiveConnectExtension::hasProperty(ScriptableExtension *, quint64 objId, const QString &propName) 0211 { 0212 QVariant val = get(nullptr, objId, propName); 0213 bool ok = !val.canConvert<ScriptableExtension::Exception>(); 0214 releaseValue(val); 0215 return ok; 0216 } 0217 0218 // Note that since we wrap around a plugin, and do not implement the browser, 0219 // we do not perform XSS checks ourselves. 0220 QVariant ScriptableLiveConnectExtension::callFunctionReference(ScriptableExtension *, quint64 o, const QString &f, const ScriptableExtension::ArgList &a) 0221 { 0222 QStringList qargs; 0223 // Convert args to strings for LC use. 0224 qargs.reserve(a.size()); 0225 for (const auto &arg : a) { 0226 bool ok; 0227 qargs.append(toLC(arg, &ok)); 0228 if (!ok) { 0229 return unimplemented(); 0230 } 0231 } 0232 0233 LiveConnectExtension::Type retType; 0234 unsigned long retObjId; 0235 QString retVal; 0236 if (wrapee->call((unsigned long)o, f, qargs, retType, retObjId, retVal)) { 0237 return acquireValue(fromLC(QString(), retType, retObjId, retVal)); 0238 } else { 0239 return unimplemented(); 0240 } 0241 } 0242 0243 QVariant ScriptableLiveConnectExtension::get(ScriptableExtension *, quint64 objId, const QString &propName) 0244 { 0245 LiveConnectExtension::Type retType; 0246 unsigned long retObjId; 0247 QString retVal; 0248 if (wrapee->get((unsigned long)objId, propName, retType, retObjId, retVal)) { 0249 return acquireValue(fromLC(propName, retType, retObjId, retVal)); 0250 } else { 0251 // exception signals failure. ### inellegant 0252 return unimplemented(); 0253 } 0254 } 0255 0256 bool ScriptableLiveConnectExtension::put(ScriptableExtension *, quint64 objId, const QString &propName, const QVariant &value) 0257 { 0258 bool ok; 0259 QString val = toLC(value, &ok); 0260 if (!ok) { 0261 return false; 0262 } 0263 0264 return wrapee->put((unsigned long)objId, propName, val); 0265 } 0266 0267 QVariant ScriptableLiveConnectExtension::fromLC(const QString &name, LiveConnectExtension::Type type, unsigned long objId, const QString &value) 0268 { 0269 switch (type) { 0270 case KParts::LiveConnectExtension::TypeBool: { 0271 bool ok; 0272 int i = value.toInt(&ok); 0273 if (ok) { 0274 return QVariant(bool(i)); 0275 } 0276 return QVariant(value.toLower() == QLatin1String("true")); 0277 } 0278 case KParts::LiveConnectExtension::TypeObject: 0279 case KParts::LiveConnectExtension::TypeFunction: { 0280 if (!refCounts.contains(objId)) { 0281 refCounts[objId] = 0; 0282 } 0283 0284 Object o = ScriptableExtension::Object(this, objId); 0285 if (type == KParts::LiveConnectExtension::TypeObject) { 0286 return QVariant::fromValue(o); 0287 } else { 0288 return QVariant::fromValue(FunctionRef(o, name)); 0289 } 0290 } 0291 0292 case KParts::LiveConnectExtension::TypeNumber: 0293 return QVariant(value.toDouble()); 0294 0295 case KParts::LiveConnectExtension::TypeString: 0296 return QVariant(value); 0297 0298 case KParts::LiveConnectExtension::TypeVoid: 0299 default: 0300 return QVariant::fromValue(ScriptableExtension::Undefined()); 0301 } 0302 } 0303 0304 QString ScriptableLiveConnectExtension::toLC(const QVariant &in, bool *ok) 0305 { 0306 *ok = true; // most of the time. 0307 0308 // Objects (or exceptions) can't be converted 0309 if (in.canConvert<ScriptableExtension::Object>() || in.canConvert<ScriptableExtension::Exception>() || in.canConvert<ScriptableExtension::FunctionRef>()) { 0310 *ok = false; 0311 return QString(); 0312 } 0313 0314 // Convert null and undefined to appropriate strings 0315 // ### this matches old KHTML behavior, but is this sensible? 0316 if (in.canConvert<ScriptableExtension::Null>()) { 0317 return QStringLiteral("null"); 0318 } 0319 0320 if (in.canConvert<ScriptableExtension::Undefined>()) { 0321 return QStringLiteral("undefined"); 0322 } 0323 0324 if (in.type() == QVariant::Bool) { 0325 return in.toBool() ? QStringLiteral("true") : QStringLiteral("false"); 0326 } 0327 0328 // Just stringify everything else, makes sense for nums as well. 0329 if (in.canConvert<QString>()) { 0330 return in.toString(); 0331 } 0332 0333 // something really icky... 0334 *ok = false; 0335 return QString(); 0336 } 0337 0338 void ScriptableLiveConnectExtension::acquire(quint64 objId) 0339 { 0340 ++refCounts[objId]; 0341 } 0342 0343 void ScriptableLiveConnectExtension::release(quint64 objId) 0344 { 0345 int newRC = --refCounts[objId]; 0346 if (!newRC) { 0347 if (objId != 0) { 0348 wrapee->unregister((unsigned long)objId); 0349 } 0350 refCounts.remove(objId); 0351 } 0352 } 0353 0354 void ScriptableLiveConnectExtension::liveConnectEvent(const unsigned long, const QString &event, const LiveConnectExtension::ArgList &args) 0355 { 0356 // We want to evaluate in the enclosure's context. 0357 QVariant enclosure = enclosingObject(); 0358 if (!enclosure.canConvert<Object>()) { 0359 releaseValue(enclosure); 0360 // qDebug() << "No enclosure, can't evaluate"; 0361 return; 0362 } 0363 0364 Object enclosureObj = enclosure.value<Object>(); 0365 0366 if (!host()->isScriptLanguageSupported(ECMAScript)) { 0367 releaseValue(enclosure); 0368 // qDebug() << "Host can't evaluate ECMAScript"; 0369 } 0370 0371 // Compute a string to evaluate. We need to escape a lot of stuff 0372 // since we're composing a bunch of strings into one. 0373 QString script = event + QLatin1Char('('); 0374 0375 LiveConnectExtension::ArgList::const_iterator i = args.begin(); 0376 const LiveConnectExtension::ArgList::const_iterator argsBegin = i; 0377 const LiveConnectExtension::ArgList::const_iterator argsEnd = args.end(); 0378 0379 for (; i != argsEnd; ++i) { 0380 if (i != argsBegin) { 0381 script += QLatin1Char(','); 0382 } 0383 if ((*i).first == KParts::LiveConnectExtension::TypeString) { 0384 script += QLatin1Char('"') + QString((*i).second).replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char('"'), QLatin1String("\\\"")) 0385 + QLatin1Char('"'); 0386 } else { 0387 script += (*i).second; 0388 } 0389 } 0390 script += QLatin1Char(')'); 0391 0392 // qDebug() << script; 0393 0394 // Ask host to evaluate.. (unfortunately, we can't do anything with the result, 0395 // but anything that uses this interface isn't expective one in the first place) 0396 QVariant result = host()->evaluateScript(this, enclosureObj.objId, script); 0397 0398 releaseValue(result); 0399 releaseValue(enclosure); 0400 } 0401 0402 // hash functions 0403 // ---------------------------------------------------------------------------- 0404 0405 unsigned int qHash(const KParts::ScriptableExtension::Object &o, uint seed) 0406 { 0407 return qHash(qMakePair(o.owner, o.objId), seed); 0408 } 0409 0410 unsigned int qHash(const KParts::ScriptableExtension::FunctionRef &f) 0411 { 0412 return qHash(qMakePair(f.base, f.field)); 0413 } 0414 0415 } // namespace KParts 0416 0417 #include "moc_scriptableextension.cpp" 0418 #include "moc_scriptableextension_p.cpp"