File indexing completed on 2024-04-28 15:23:16

0001 /* This file is part of the KDE project
0002    Copyright (C) 2010 Maksim Orlovich <maksim@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kjs_scriptable.h"
0021 #include "kjs_binding.h"
0022 #include "kjs_proxy.h"
0023 #include "kjs_dom.h"
0024 #include "khtmlpart_p.h"
0025 
0026 namespace KJS
0027 {
0028 
0029 static QVariant scriptableNull()
0030 {
0031     return QVariant::fromValue(ScriptableExtension::Null());
0032 }
0033 
0034 static bool isException(const QVariant &v)
0035 {
0036     return v.canConvert<ScriptableExtension::Exception>();
0037 }
0038 
0039 static bool isFuncRef(const QVariant &v)
0040 {
0041     return v.canConvert<ScriptableExtension::FunctionRef>();
0042 }
0043 
0044 static bool isForeignObject(const QVariant &v)
0045 {
0046     return v.canConvert<ScriptableExtension::Object>();
0047 }
0048 
0049 QVariant exception(const char *msg)
0050 {
0051     qCWarning(KHTML_LOG) << msg;
0052     return QVariant::fromValue(ScriptableExtension::Exception(QString::fromLatin1(msg)));
0053 }
0054 
0055 //------------------------------------------------------------------------------
0056 
0057 // will return with owner = 0 on failure.
0058 static ScriptableExtension::Object grabRoot(ScriptableExtension *ext)
0059 {
0060     ScriptableExtension::Object o;
0061 
0062     if (!ext) {
0063         return o;
0064     }
0065 
0066     // Grab root, make sure it's an actual object.
0067     QVariant root = ext->rootObject();
0068     if (!isForeignObject(root)) {
0069         // Might still need to release it if it's a function
0070         ScriptableExtension::releaseValue(root);
0071 
0072         return o;
0073     }
0074 
0075     o = root.value<ScriptableExtension::Object>();
0076     return o;
0077 }
0078 
0079 // a couple helpers for client code.
0080 bool pluginRootGet(ExecState *exec, ScriptableExtension *ext, const KJS::Identifier &i, PropertySlot &slot)
0081 {
0082     ScriptableExtension::Object rootObj = grabRoot(ext);
0083     if (!rootObj.owner) {
0084         return false;
0085     }
0086 
0087     QVariant v = rootObj.owner->get(nullptr /* ### we don't expect leaves to check credentials*/,
0088                                     rootObj.objId, i.qstring());
0089 
0090     bool ok = false;
0091     if (!isException(v)) {
0092         getImmediateValueSlot(nullptr, ScriptableOperations::importValue(exec, v, true), slot);
0093         ok = true;
0094     }
0095 
0096     rootObj.owner->release(rootObj.objId);
0097     return ok;
0098 }
0099 
0100 bool pluginRootPut(ExecState * /*exec*/, ScriptableExtension *ext, const KJS::Identifier &i, JSValue *v)
0101 {
0102     ScriptableExtension::Object rootObj = grabRoot(ext);
0103     if (!rootObj.owner) {
0104         return false;
0105     }
0106 
0107     QVariant qv = ScriptableOperations::exportValue(v, true);
0108     bool ok = rootObj.owner->put(nullptr, rootObj.objId, i.qstring(), qv);
0109     ScriptableExtension::releaseValue(qv);
0110 
0111     rootObj.owner->release(rootObj.objId);
0112     return ok;
0113 }
0114 
0115 //------------------------------------------------------------------------------
0116 // KJS peer wrapping external objects
0117 
0118 const ClassInfo WrapScriptableObject::info = { " WrapScriptableObject", nullptr, nullptr, nullptr };
0119 
0120 WrapScriptableObject::WrapScriptableObject(ExecState * /*exec*/, Type t,
0121         ScriptableExtension *owner, quint64 objId,
0122         const QString &field):
0123     objExtension(owner), objId(objId), field(field), type(t), refsByUs(1), tableKey(owner)
0124 {
0125     owner->acquire(objId);
0126 }
0127 
0128 WrapScriptableObject::~WrapScriptableObject()
0129 {
0130     if (ScriptableExtension *o = objExtension.data()) {
0131         for (int r = 0; r < refsByUs; ++r) {
0132             o->release(objId);
0133         }
0134     }
0135 
0136     ScriptableExtension::Object obj(tableKey, objId);
0137     if (type == Object) {
0138         ScriptableOperations::importedObjects()->remove(obj);
0139     } else {
0140         ScriptableOperations::importedFunctions()->remove(ScriptableExtension::FunctionRef(obj, field));
0141     }
0142 }
0143 
0144 void WrapScriptableObject::reportRef()
0145 {
0146     ++refsByUs;
0147 }
0148 
0149 QVariant WrapScriptableObject::doGet(ExecState *exec, const ScriptableExtension::Object &o,
0150                                      const QString &field, bool *ok)
0151 {
0152     *ok = false;
0153 
0154     if (!o.owner) { // such as when was constructed from null .data();
0155         return QVariant();
0156     }
0157 
0158     QVariant v = o.owner->get(principal(exec), o.objId, field);
0159 
0160     if (!isException(v)) {
0161         *ok = true;
0162     }
0163     return v;
0164 }
0165 
0166 ScriptableExtension::Object WrapScriptableObject::resolveReferences(
0167     ExecState *exec,
0168     const ScriptableExtension::FunctionRef &f,
0169     bool *ok)
0170 {
0171     QVariant v = doGet(exec, f.base, f.field, ok);
0172     if (*ok) {
0173         // See what sort of type we got.
0174         if (isForeignObject(v)) {
0175             return v.value<ScriptableExtension::Object>();
0176         } else if (isFuncRef(v)) {
0177             return resolveReferences(exec, v.value<ScriptableExtension::FunctionRef>(), ok);
0178         } else {
0179             // We got a primitive. We don't care for those.
0180             *ok = false;
0181         }
0182     }
0183     return ScriptableExtension::Object();
0184 }
0185 
0186 ScriptableExtension::Object WrapScriptableObject::resolveAnyReferences(ExecState *exec, bool *ok)
0187 {
0188     ScriptableExtension::Object obj(objExtension.data(), objId);
0189 
0190     if (type == FunctionRef) {
0191         obj = resolveReferences(exec, ScriptableExtension::FunctionRef(obj, field), ok);
0192     }
0193 
0194     if (!obj.owner) {
0195         *ok = false;
0196     }
0197 
0198     return obj;
0199 }
0200 
0201 bool WrapScriptableObject::getOwnPropertySlot(ExecState *exec, const Identifier &i,
0202         PropertySlot &slot)
0203 {
0204     bool ok;
0205     ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
0206 
0207     if (!ok) {
0208         return false;
0209     }
0210 
0211     QVariant v = doGet(exec, actualObj, i.qstring(), &ok);
0212 
0213     if (!ok) {
0214         return false;
0215     }
0216 
0217     return getImmediateValueSlot(this, ScriptableOperations::importValue(exec, v, true), slot);
0218 }
0219 
0220 void WrapScriptableObject::put(ExecState *exec, const Identifier &i, JSValue *value, int)
0221 {
0222     // ### Do we swallow failure, or what?
0223     bool ok;
0224     ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
0225 
0226     if (!ok) {
0227         return;
0228     }
0229 
0230     QVariant sv = ScriptableOperations::exportValue(value, true);
0231     actualObj.owner->put(principal(exec), actualObj.objId, i.qstring(), sv);
0232     ScriptableExtension::releaseValue(sv);
0233 }
0234 
0235 bool WrapScriptableObject::deleteProperty(ExecState *exec, const Identifier &i)
0236 {
0237     bool ok;
0238     ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
0239 
0240     if (!ok) {
0241         return false;
0242     }
0243 
0244     return actualObj.owner->removeProperty(principal(exec),
0245                                            actualObj.objId, i.qstring());
0246 }
0247 
0248 ScriptableExtension::ArgList WrapScriptableObject::exportArgs(const List &l)
0249 {
0250     ScriptableExtension::ArgList ol;
0251     for (int p = 0; p < l.size(); ++p) {
0252         ol.append(ScriptableOperations::exportValue(l.at(p), true));
0253     }
0254     return ol;
0255 }
0256 
0257 void WrapScriptableObject::releaseArgs(ScriptableExtension::ArgList &a)
0258 {
0259     for (int p = 0; p < a.size(); ++p) {
0260         ScriptableOperations::releaseValue(a[p]);
0261     }
0262 }
0263 
0264 JSValue *WrapScriptableObject::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
0265 {
0266     QVariant res;
0267 
0268     if (ScriptableExtension *base = objExtension.data()) {
0269         ScriptableExtension::ArgList sargs = exportArgs(args);
0270         if (type == Object) {
0271             res = base->callAsFunction(principal(exec), objId, sargs);
0272         } else {
0273             res = base->callFunctionReference(principal(exec), objId, field, sargs);
0274         }
0275         releaseArgs(sargs);
0276     }
0277 
0278     // Problem. Throw an exception.
0279     if (!res.isValid() || isException(res)) {
0280         return throwError(exec, GeneralError, "Call to plugin function failed");
0281     } else {
0282         return ScriptableOperations::importValue(exec, res, true);
0283     }
0284 }
0285 
0286 JSObject *WrapScriptableObject::construct(ExecState *exec, const List &args)
0287 {
0288     QVariant res;
0289 
0290     bool ok;
0291     ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
0292     if (ok) {
0293         ScriptableExtension::ArgList sargs = exportArgs(args);
0294         res = actualObj.owner->callAsConstructor(principal(exec), actualObj.objId,
0295                 sargs);
0296         releaseArgs(sargs);
0297     }
0298 
0299     if (!res.isValid() || isException(res)) {
0300         return throwError(exec, GeneralError, "Call to plugin ctor failed");
0301     } else {
0302         JSValue *v = ScriptableOperations::importValue(exec, res, true);
0303         return v->toObject(exec);
0304     }
0305 }
0306 
0307 void WrapScriptableObject::getOwnPropertyNames(ExecState *exec, PropertyNameArray &a, PropertyMap::PropertyMode mode)
0308 {
0309     JSObject::getOwnPropertyNames(exec, a, mode);
0310 
0311     bool ok;
0312     ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
0313     if (ok) {
0314         QStringList out;
0315         if (actualObj.owner->enumerateProperties(principal(exec), actualObj.objId, &out)) {
0316             foreach (const QString &s, out) {
0317                 a.add(Identifier(s));
0318             }
0319         }
0320     }
0321 }
0322 
0323 UString WrapScriptableObject::toString(ExecState *) const
0324 {
0325     QString iface;
0326     if (ScriptableExtension *se = objExtension.data()) {
0327         iface = QString::fromLatin1(se->metaObject()->className());
0328     } else {
0329         iface = QString::fromLatin1("detached");
0330     }
0331 
0332     if (type == FunctionRef) {
0333         return QString(QLatin1String("[function ImportedScriptable:") + iface + QLatin1Char('/') + field + QLatin1Char(']'));
0334     } else {
0335         return QString(QLatin1String("[object ImportedScriptable:") + iface + QLatin1Char(']'));
0336     }
0337 }
0338 
0339 ScriptableExtension *WrapScriptableObject::principal(ExecState *exec)
0340 {
0341     KJS::ScriptInterpreter *si = static_cast<KJS::ScriptInterpreter *>(exec->dynamicInterpreter());
0342     KParts::ReadOnlyPart   *part = si->part();
0343     if (!part) {
0344         return nullptr;
0345     }
0346 
0347     return ScriptableExtension::childObject(part);
0348 }
0349 
0350 //-----------------------------------------------------------------------------
0351 // conversion stuff
0352 
0353 /**
0354    SECURITY: For the conversion helpers, it is assumed that 'exec' corresponds
0355    to the appropriate principal.
0356 */
0357 
0358 JSObject *ScriptableOperations::importObject(ExecState *exec, const QVariant &v, bool alreadyRefd)
0359 {
0360     ScriptableExtension::Object obj = v.value<ScriptableExtension::Object>();
0361     if (JSObject *our = tryGetNativeObject(obj)) {
0362         return our;
0363     } else {
0364         // Create a wrapper, and register the import, this is just for
0365         // hashconsing.
0366         if (WrapScriptableObject *old = importedObjects()->value(obj)) {
0367             if (alreadyRefd) {
0368                 old->reportRef();
0369             }
0370             return old;
0371         } else {
0372             WrapScriptableObject *wrap =
0373                 new WrapScriptableObject(exec, WrapScriptableObject::Object,
0374                                          obj.owner, obj.objId);
0375             importedObjects()->insert(obj, wrap);
0376             if (alreadyRefd) {
0377                 wrap->reportRef();
0378             }
0379             return wrap;
0380         }
0381     }
0382 }
0383 
0384 // Note: this is used to convert a full function ref to a value,
0385 // which loses the this-binding in the native case.  We do keep the pair in the
0386 // external case, inside the wrapper,  since the method might not have an own
0387 // name, and we'd like to be able to refer to it.
0388 JSValue *ScriptableOperations::importFunctionRef(ExecState *exec, const QVariant &v, bool /*alreadyRefd*/)
0389 {
0390     ScriptableExtension::FunctionRef fr = v.value<ScriptableExtension::FunctionRef>();
0391 
0392     if (JSObject *base = tryGetNativeObject(fr.base)) {
0393         return base->get(exec, Identifier(fr.field));
0394     } else {
0395         if (WrapScriptableObject *old = importedFunctions()->value(fr)) {
0396             return old;
0397         } else {
0398             WrapScriptableObject *wrap =
0399                 new WrapScriptableObject(exec, WrapScriptableObject::FunctionRef,
0400                                          fr.base.owner, fr.base.objId, fr.field);
0401             importedFunctions()->insert(fr, wrap);
0402             return wrap;
0403         }
0404     }
0405 }
0406 
0407 JSValue *ScriptableOperations::importValue(ExecState *exec, const QVariant &v, bool alreadyRefd)
0408 {
0409     if (v.canConvert<ScriptableExtension::FunctionRef>()) {
0410         return importFunctionRef(exec, v, alreadyRefd);
0411     }
0412     if (v.canConvert<ScriptableExtension::Object>()) {
0413         return importObject(exec, v, alreadyRefd);
0414     }
0415     if (v.canConvert<ScriptableExtension::Null>()) {
0416         return jsNull();
0417     }
0418     if (v.canConvert<ScriptableExtension::Undefined>()) {
0419         return jsUndefined();
0420     }
0421     if (v.type() == QVariant::Bool) {
0422         return jsBoolean(v.toBool());
0423     }
0424     if (v.type() == QVariant::String) {
0425         return jsString(v.toString());
0426     }
0427     if (v.canConvert<double>()) {
0428         return jsNumber(v.toDouble());
0429     }
0430     qCWarning(KHTML_LOG) << "conversion from " << v << "failed";
0431     return jsNull();
0432 }
0433 
0434 List ScriptableOperations::importArgs(ExecState *exec, const ArgList &args)
0435 {
0436     // Args are not pre-ref'd for us..
0437     List out;
0438     for (int i = 0; i < args.size(); ++i) {
0439         out.append(importValue(exec, args[i], false));
0440     }
0441     return out;
0442 }
0443 
0444 ScriptableExtension::Object ScriptableOperations::exportNativeObject(JSObject *o, bool preRef)
0445 {
0446     assert(!o->inherits(&WrapScriptableObject::info));
0447     // We're exporting our own. Add to export table if needed.
0448     // Use the pointer as an ID.
0449     if (!exportedObjects()->contains(o)) {
0450         exportedObjects()->insert(o, 0);
0451     }
0452     if (preRef) {
0453         ++(*exportedObjects())[o];
0454     }
0455 
0456     return ScriptableExtension::Object(ScriptableOperations::self(),
0457                                        reinterpret_cast<quint64>(o));
0458 }
0459 
0460 QVariant ScriptableOperations::exportObject(JSObject *o, bool preRef)
0461 {
0462     // XSS checks are done at get time, so if we have a value here, we can
0463     // export it.
0464     if (o->inherits(&WrapScriptableObject::info)) {
0465         // Re-exporting external one. That's easy.
0466         WrapScriptableObject *wo = static_cast<WrapScriptableObject *>(o);
0467         if (ScriptableExtension *owner = wo->objExtension.data()) {
0468             QVariant v = QVariant::fromValue(ScriptableExtension::Object(owner, wo->objId));
0469             if (preRef) {
0470                 acquireValue(v);
0471             }
0472             return v;
0473         } else {
0474             qCWarning(KHTML_LOG) << "export of an object of a destroyed extension. Returning null";
0475             return scriptableNull();
0476         }
0477     } else {
0478         return QVariant::fromValue(exportNativeObject(o, preRef));
0479     }
0480 }
0481 
0482 QVariant ScriptableOperations::exportFuncRef(JSObject *base, const QString &field, bool preRef)
0483 {
0484     ScriptableExtension::Object exportBase = exportNativeObject(base, preRef);
0485     return QVariant::fromValue(ScriptableExtension::FunctionRef(exportBase, field));
0486 }
0487 
0488 QVariant ScriptableOperations::exportValue(JSValue *v, bool preRef)
0489 {
0490     switch (v->type()) {
0491     case NumberType:
0492         return QVariant::fromValue(v->getNumber());
0493     case BooleanType:
0494         return QVariant::fromValue(v->getBoolean());
0495     case NullType:
0496         return QVariant::fromValue(ScriptableExtension::Null());
0497     case StringType:
0498         return QVariant::fromValue(v->getString().qstring());
0499     case ObjectType:
0500         return exportObject(v->getObject(), preRef);
0501     case UndefinedType:
0502     default:
0503         return QVariant::fromValue(ScriptableExtension::Undefined());
0504     }
0505 }
0506 
0507 //-----------------------------------------------------------------------------
0508 // operations
0509 QHash<JSObject *, int> *ScriptableOperations::s_exportedObjects = nullptr;
0510 QHash<ScriptableExtension::Object, WrapScriptableObject *> *ScriptableOperations::s_importedObjects   = nullptr;
0511 QHash<ScriptableExtension::FunctionRef, WrapScriptableObject *> *ScriptableOperations::s_importedFunctions = nullptr;
0512 ScriptableOperations *ScriptableOperations::s_instance = nullptr;
0513 
0514 QHash<ScriptableExtension::Object, WrapScriptableObject *> *ScriptableOperations::importedObjects()
0515 {
0516     if (!s_importedObjects) {
0517         s_importedObjects = new QHash<Object, WrapScriptableObject *>;
0518     }
0519     return s_importedObjects;
0520 }
0521 
0522 QHash<ScriptableExtension::FunctionRef, WrapScriptableObject *> *ScriptableOperations::importedFunctions()
0523 {
0524     if (!s_importedFunctions) {
0525         s_importedFunctions = new QHash<FunctionRef, WrapScriptableObject *>();
0526     }
0527     return s_importedFunctions;
0528 }
0529 
0530 // A little helper for marking exportedObjects. We need this since we have
0531 // an unclear runtime with respect to interpreter.
0532 class ScriptableOperationsMarker: public JSObject
0533 {
0534 public:
0535     void mark() override
0536     {
0537         JSObject::mark();
0538         ScriptableOperations::self()->mark();
0539     }
0540 };
0541 
0542 QHash<JSObject *, int> *ScriptableOperations::exportedObjects()
0543 {
0544     if (!s_exportedObjects) {
0545         s_exportedObjects = new QHash<JSObject *, int>;
0546 
0547         gcProtect(new ScriptableOperationsMarker());
0548     }
0549     return s_exportedObjects;
0550 }
0551 
0552 ScriptableOperations *ScriptableOperations::self()
0553 {
0554     if (!s_instance) {
0555         s_instance = new ScriptableOperations;
0556     }
0557     return s_instance;
0558 }
0559 
0560 ScriptableOperations::ScriptableOperations(): ScriptableExtension(nullptr)
0561 {}
0562 
0563 ScriptableOperations::~ScriptableOperations()
0564 {
0565     assert(false);
0566 }
0567 
0568 JSObject *ScriptableOperations::tryGetNativeObject(const Object &sObj)
0569 {
0570     if (ScriptableOperations *o = qobject_cast<ScriptableOperations *>(sObj.owner)) {
0571         return o->objectForId(sObj.objId);
0572     } else {
0573         return nullptr;
0574     }
0575 }
0576 
0577 QVariant ScriptableOperations::handleReturn(ExecState *exec, JSValue *v)
0578 {
0579     if (exec->hadException()) {
0580         JSValue *e = exec->exception();
0581         exec->clearException();
0582 
0583         QString msg = QLatin1String("KJS exception");
0584 
0585         if (JSObject *eo = e->getObject()) {
0586             JSValue *msgVal = eo->get(exec, exec->propertyNames().message);
0587             if (!msgVal->isUndefined()) {
0588                 msg = msgVal->toString(exec).qstring();
0589             }
0590 
0591             // in case the get failed too.
0592             exec->clearException();
0593         }
0594 
0595         return QVariant::fromValue(ScriptableExtension::Exception(msg));
0596     }
0597 
0598     return exportValue(v, true);
0599 }
0600 
0601 QVariant ScriptableOperations::callAsFunction(ScriptableExtension *caller,
0602         quint64 objId, const ArgList &args)
0603 {
0604     ExecState *exec = execStateForPrincipal(caller);
0605     if (!exec) {
0606         return exception("No scripting context or frame");
0607     }
0608 
0609     JSObject *fn = objectForId(objId);
0610     if (!fn || !fn->implementsCall()) {
0611         return exception("Call on a non-object or non-calleable");
0612     }
0613 
0614     JSValue *res = fn->callAsFunction(exec, exec->dynamicInterpreter()->globalObject(),
0615                                       importArgs(exec, args));
0616     return handleReturn(exec, res);
0617 }
0618 
0619 QVariant ScriptableOperations::callAsConstructor(ScriptableExtension *caller, quint64 objId, const ArgList &args)
0620 {
0621     ExecState *exec = execStateForPrincipal(caller);
0622     if (!exec) {
0623         return exception("No scripting context or frame");
0624     }
0625 
0626     JSObject *fn = objectForId(objId);
0627     if (!fn || !fn->implementsConstruct()) {
0628         return exception("new on a non-constructor");
0629     }
0630 
0631     JSValue *res = fn->construct(exec, importArgs(exec, args));
0632     return handleReturn(exec, res);
0633 }
0634 
0635 QVariant ScriptableOperations::callFunctionReference(ScriptableExtension *caller,
0636         quint64 objId, const QString &f, const ArgList &args)
0637 {
0638     ExecState *exec = execStateForPrincipal(caller);
0639 
0640     if (!exec) {
0641         return exception("No scripting context or frame");
0642     }
0643 
0644     JSObject *base = objectForId(objId);
0645     if (!base) {
0646         return exception("Call with an invalid base");
0647     }
0648 
0649     JSValue *kid = base->get(exec, Identifier(f));
0650     if (!kid->isObject() || exec->hadException() || !kid->getObject()->implementsCall()) {
0651         exec->clearException();
0652         return exception("Reference did not resolve to a function");
0653     }
0654 
0655     // Whee..
0656     JSValue *res = kid->getObject()->callAsFunction(exec, base, importArgs(exec, args));
0657     return handleReturn(exec, res);
0658 }
0659 
0660 bool ScriptableOperations::hasProperty(ScriptableExtension *caller, quint64 objId, const QString &propName)
0661 {
0662     ExecState *exec = execStateForPrincipal(caller);
0663     if (!exec) {
0664         exception("No scripting context or frame");
0665         return false;
0666     }
0667 
0668     JSObject *o = objectForId(objId);
0669     if (!o) {
0670         exception("hasProperty on a non-object");
0671         return false;
0672     }
0673 
0674     return o->hasProperty(exec, Identifier(propName));
0675 }
0676 
0677 QVariant ScriptableOperations::get(ScriptableExtension *caller, quint64 objId, const QString &propName)
0678 {
0679     ExecState *exec = execStateForPrincipal(caller);
0680     if (!exec) {
0681         return exception("No scripting context or frame");
0682     }
0683 
0684     JSObject *o = objectForId(objId);
0685     if (!o) {
0686         return exception("get on a non-object");
0687     }
0688 
0689     JSValue *v = o->get(exec, Identifier(propName));
0690     if (!exec->hadException() && v->isObject() && v->getObject()->implementsCall()) {
0691         // For a function we got OK, return a reference.
0692         return exportFuncRef(o, propName, true);
0693     } else {
0694         // straight return for other stuff or failure
0695         return handleReturn(exec, v);
0696     }
0697 }
0698 
0699 bool ScriptableOperations::put(ScriptableExtension *caller, quint64 objId,
0700                                const QString &propName, const QVariant &value)
0701 {
0702     ExecState *exec = execStateForPrincipal(caller);
0703     if (!exec) {
0704         return false;    // ### debug warn?
0705     }
0706 
0707     JSObject *o = objectForId(objId);
0708     if (!o) {
0709         return false;
0710     }
0711 
0712     o->put(exec, Identifier(propName), importValue(exec, value, false));
0713     if (!exec->hadException()) {
0714         return true;
0715     } else {
0716         exec->clearException();
0717         return false;
0718     }
0719 }
0720 
0721 bool ScriptableOperations::removeProperty(ScriptableExtension *caller,
0722         quint64 objId, const QString &propName)
0723 {
0724     ExecState *exec = execStateForPrincipal(caller);
0725     if (!exec) {
0726         return false;    // ### debug warn?
0727     }
0728 
0729     JSObject *o = objectForId(objId);
0730     if (!o) {
0731         return false;
0732     }
0733 
0734     bool ok = o->deleteProperty(exec, Identifier(propName));
0735     if (exec->hadException()) {
0736         exec->clearException();
0737         ok = false;
0738     }
0739     return ok;
0740 }
0741 
0742 bool ScriptableOperations::enumerateProperties(ScriptableExtension *caller,
0743         quint64 objId, QStringList *result)
0744 {
0745     ExecState *exec = execStateForPrincipal(caller);
0746     if (!exec) {
0747         return false;    // ### debug warn?
0748     }
0749 
0750     JSObject *o = objectForId(objId);
0751     if (!o) {
0752         return false;
0753     }
0754 
0755     PropertyNameArray pa;
0756     o->getPropertyNames(exec, pa);
0757 
0758     for (int i = 0; i < pa.size(); ++i) {
0759         result->append(pa[i].qstring());
0760     }
0761     return true;
0762 }
0763 
0764 KHTMLPart *ScriptableOperations::partForPrincipal(ScriptableExtension *caller)
0765 {
0766     // We implement our security checks by delegating to the KHTMLPart corresponding
0767     // to the given plugin's principal (which is the KHTMLPart owning it), and letting
0768     // the underlying implementation perform them (which it has to anyway)
0769 
0770     if (KHTMLPartScriptable *o = qobject_cast<KHTMLPartScriptable *>(caller)) {
0771         return o->m_part;
0772     } else {
0773         // We always set the host on child extensions.
0774         return partForPrincipal(caller->host());
0775     }
0776 }
0777 
0778 ExecState *ScriptableOperations::execStateForPrincipal(ScriptableExtension *caller)
0779 {
0780     KHTMLPart *part = partForPrincipal(caller);
0781 
0782     if (!part) {
0783         return nullptr;
0784     }
0785 
0786     KJSProxy *proxy = KJSProxy::proxy(part);
0787     if (!proxy) {
0788         return nullptr;
0789     }
0790 
0791     KJS::Interpreter *i = proxy->interpreter();
0792     if (!i) {
0793         return nullptr;
0794     }
0795 
0796     return i->globalExec();
0797 }
0798 
0799 void ScriptableOperations::acquire(quint64 objId)
0800 {
0801     JSObject *ptr = objectForId(objId);
0802 
0803     if (ptr) {
0804         ++(*exportedObjects())[ptr];
0805     } else {
0806         assert(false);
0807     }
0808 }
0809 
0810 void ScriptableOperations::release(quint64 objId)
0811 {
0812     JSObject *ptr = objectForId(objId);
0813 
0814     if (ptr) {
0815         int newRC = --(*exportedObjects())[ptr];
0816         if (newRC == 0) {
0817             exportedObjects()->remove(ptr);
0818         }
0819     } else {
0820         assert(false);
0821     }
0822 }
0823 
0824 JSObject *ScriptableOperations::objectForId(quint64 objId)
0825 {
0826     JSObject *ptr = reinterpret_cast<JSObject *>(objId);
0827 
0828     // Verify the pointer against the exports table for paranoia.
0829     if (exportedObjects()->contains(ptr)) {
0830         return ptr;
0831     } else {
0832         assert(false);
0833         return nullptr;
0834     }
0835 }
0836 
0837 void ScriptableOperations::mark()
0838 {
0839     QHash<JSObject *, int> *exp = exportedObjects();
0840 
0841     for (QHash<JSObject *, int>::iterator i = exp->begin(); i != exp->end(); ++i) {
0842         JSObject *o = i.key();
0843         if (i.value() && !o->marked()) {
0844             o->mark();
0845         }
0846     }
0847 }
0848 
0849 //-----------------------------------------------------------------------------
0850 // per-part stuff.
0851 
0852 KHTMLPartScriptable::KHTMLPartScriptable(KHTMLPart *part):
0853     ScriptableExtension(part), m_part(part)
0854 {
0855 }
0856 
0857 KJS::Interpreter *KHTMLPartScriptable::interpreter()
0858 {
0859     KJSProxy *proxy = KJSProxy::proxy(m_part);
0860     if (!proxy) {
0861         return nullptr;
0862     }
0863 
0864     return proxy->interpreter();
0865 }
0866 
0867 QVariant KHTMLPartScriptable::rootObject()
0868 {
0869     if (KJS::Interpreter *i = interpreter()) {
0870         return ScriptableOperations::exportObject(i->globalObject(), true);
0871     }
0872 
0873     return scriptableNull();
0874 }
0875 
0876 QVariant KHTMLPartScriptable::encloserForKid(KParts::ScriptableExtension *kid)
0877 {
0878     ReadOnlyPart *childPart = qobject_cast<ReadOnlyPart *>(kid->parent());
0879 
0880     KJS::Interpreter *i = interpreter();
0881     if (!childPart || !i) {
0882         return scriptableNull();
0883     }
0884 
0885     khtml::ChildFrame *f = m_part->frame(childPart);
0886 
0887     if (!f) {
0888         qCWarning(KHTML_LOG) << "unable to find frame. Huh?";
0889         return scriptableNull();
0890     }
0891 
0892     // ### should this deal with fake window objects for iframes?
0893     // ### this should never actually get an iframe once iframes are fixed
0894     if (!f->m_partContainerElement.isNull()) {
0895         return ScriptableOperations::exportValue(
0896                    getDOMNode(i->globalExec(), f->m_partContainerElement.data()), true);
0897     }
0898 
0899     qCWarning(KHTML_LOG) << "could not find the part container";
0900     return scriptableNull();
0901 }
0902 
0903 // For paranoia: forward to ScriptOperations
0904 void KHTMLPartScriptable::acquire(quint64 objid)
0905 {
0906     ScriptableOperations::self()->acquire(objid);
0907 }
0908 
0909 void KHTMLPartScriptable::release(quint64 objid)
0910 {
0911     ScriptableOperations::self()->release(objid);
0912 }
0913 
0914 QVariant KHTMLPartScriptable::evaluateScript(ScriptableExtension *caller,
0915         quint64 contextObjectId,
0916         const QString &code,
0917         ScriptLanguage lang)
0918 {
0919     // qCDebug(KHTML_LOG) << code;
0920 
0921     if (lang != ECMAScript) {
0922         return exception("unsupported language");
0923     }
0924 
0925     KHTMLPart *callingHtmlPart = ScriptableOperations::partForPrincipal(caller);
0926     if (!callingHtmlPart) {
0927         return exception("failed to resolve principal");
0928     }
0929 
0930     // Figure out the object we want to access, and its corresponding part.
0931     JSObject *o = ScriptableOperations::objectForId(contextObjectId);
0932     if (!o) {
0933         return exception("invalid object");
0934     }
0935 
0936     DOM::NodeImpl *node = toNode(o);
0937 
0938     // Presently, we only permit node contexts here.
0939     // ### TODO: window contexts?
0940     if (!node) {
0941         return exception("non-Node context");
0942     }
0943 
0944     KHTMLPart *targetPart = node->document()->part();
0945 
0946     if (!targetPart) {
0947         return exception("failed to resolve destination principal");
0948     }
0949 
0950     if (!targetPart->checkFrameAccess(callingHtmlPart)) {
0951         return exception("XSS check failed");
0952     }
0953 
0954     // wheee..
0955     targetPart->executeScript(DOM::Node(node), code);
0956 
0957     // ### TODO: Return value. Which is a completely different kind of QVariant.
0958     // might just want to go to kJSProxy directly
0959 
0960     return scriptableNull();
0961 }
0962 
0963 bool KHTMLPartScriptable::isScriptLanguageSupported(ScriptLanguage lang) const
0964 {
0965     return lang == ECMAScript;
0966 }
0967 
0968 bool KHTMLPartScriptable::setException(ScriptableExtension * /*caller*/,
0969                                        const QString &message)
0970 {
0971     qCWarning(KHTML_LOG) << "ignoring:" << message;
0972     return false;
0973 }
0974 
0975 } // namespace KJS
0976 
0977 #include "moc_kjs_scriptable.cpp"