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"