File indexing completed on 2023-10-03 03:17:45
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"