A fairly large patch, but mostly moving things. In order to allow

us to use QScriptEngines from other sources (such as QML) we need
to change the code so that we don't have a custom QScriptEngine
subclass - only one codebase can do that at a time. The scriptenv
is now a normal QObject and contains a QScriptEngine instead of
inheriting from it.

svn path=/trunk/KDE/kdebase/runtime/; revision=1094259
This commit is contained in:
Richard J. Moore 2010-02-22 11:33:21 +00:00
parent c58131cd40
commit cc5c47e270
8 changed files with 125 additions and 73 deletions

View File

@ -27,10 +27,13 @@
#include "simplebindings/i18n.h"
JavaScriptDataEngine::JavaScriptDataEngine(QObject *parent, const QVariantList &args)
: DataEngineScript(parent),
m_qscriptEngine(new ScriptEnv(this))
: DataEngineScript(parent)
{
Q_UNUSED(args)
Q_UNUSED(args);
m_qscriptEngine = new QScriptEngine(this);
m_env = new ScriptEnv(this, m_qscriptEngine);
connect(m_qscriptEngine, SIGNAL(reportError(ScriptEnv*,bool)), this, SLOT(reportError(ScriptEnv*,bool)));
}
@ -52,7 +55,7 @@ bool JavaScriptDataEngine::init()
registerDataEngineMetaTypes(m_qscriptEngine);
Authorization auth;
if (!m_qscriptEngine->importExtensions(description(), iface, auth)) {
if (!m_env->importExtensions(description(), iface, auth)) {
return false;
}
@ -194,7 +197,7 @@ QScriptValue JavaScriptDataEngine::callFunction(const QString &functionName, con
m_qscriptEngine->popContext();
if (m_qscriptEngine->hasUncaughtException()) {
reportError(m_qscriptEngine, false);
reportError(m_env, false);
} else {
return rv;
}
@ -203,13 +206,13 @@ QScriptValue JavaScriptDataEngine::callFunction(const QString &functionName, con
return QScriptValue();
}
void JavaScriptDataEngine::reportError(ScriptEnv *engine, bool fatal) const
void JavaScriptDataEngine::reportError(ScriptEnv *env, bool fatal) const
{
Q_UNUSED(fatal)
kDebug() << "Error: " << engine->uncaughtException().toString()
<< " at line " << engine->uncaughtExceptionLineNumber() << endl;
kDebug() << engine->uncaughtExceptionBacktrace();
kDebug() << "Error: " << env->engine()->uncaughtException().toString()
<< " at line " << env->engine()->uncaughtExceptionLineNumber() << endl;
kDebug() << env->engine()->uncaughtExceptionBacktrace();
}
QStringList JavaScriptDataEngine::sources() const

View File

@ -26,6 +26,7 @@
class ScriptEnv;
class QScriptContext;
class QScriptEngine;
class JavaScriptDataEngine : public Plasma::DataEngineScript
{
@ -62,7 +63,8 @@ private:
QScriptValue callFunction(const QString &functionName, const QScriptValueList &args) const;
ScriptEnv *m_qscriptEngine;
QScriptEngine *m_qscriptEngine;
ScriptEnv *m_env;
QScriptValue iface;
};

View File

@ -35,10 +35,13 @@ Q_DECLARE_METATYPE(ConstRunnerContextStar)
Q_DECLARE_METATYPE(ConstSearchMatchStar)
JavaScriptRunner::JavaScriptRunner(QObject *parent, const QVariantList &args)
: RunnerScript(parent),
m_engine(new ScriptEnv(this))
: RunnerScript(parent)
{
Q_UNUSED(args);
m_engine = new QScriptEngine(this);
m_env = new ScriptEnv(this, m_engine);
connect(m_engine, SIGNAL(reportError(ScriptEnv*,bool)), this, SLOT(reportError(ScriptEnv*,bool)));
}
@ -56,7 +59,7 @@ bool JavaScriptRunner::init()
setupObjects();
Authorization auth;
if (!m_engine->importExtensions(description(), m_self, auth)) {
if (!m_env->importExtensions(description(), m_self, auth)) {
return false;
}
@ -71,7 +74,7 @@ bool JavaScriptRunner::init()
m_engine->evaluate(script);
if (m_engine->hasUncaughtException()) {
reportError(m_engine, true);
reportError(m_env, true);
return false;
}
@ -95,7 +98,7 @@ void JavaScriptRunner::match(Plasma::RunnerContext &search)
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine, false);
reportError(m_env, false);
}
}
@ -117,7 +120,7 @@ void JavaScriptRunner::exec(const Plasma::RunnerContext *search, const Plasma::Q
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine, false);
reportError(m_env, false);
}
}
@ -132,12 +135,12 @@ void JavaScriptRunner::setupObjects()
global.setProperty("runner", m_self);
}
void JavaScriptRunner::reportError(ScriptEnv *engine, bool fatal)
void JavaScriptRunner::reportError(ScriptEnv *env, bool fatal)
{
Q_UNUSED(fatal)
kDebug() << "Error: " << engine->uncaughtException().toString()
<< " at line " << engine->uncaughtExceptionLineNumber() << endl;
kDebug() << engine->uncaughtExceptionBacktrace();
kDebug() << "Error: " << env->engine()->uncaughtException().toString()
<< " at line " << env->engine()->uncaughtExceptionLineNumber() << endl;
kDebug() << env->engine()->uncaughtExceptionBacktrace();
}
#include "javascriptrunner.moc"

View File

@ -25,6 +25,8 @@
#include <Plasma/RunnerScript>
class QScriptEngine;
class ScriptEnv;
class JavaScriptRunner : public Plasma::RunnerScript
@ -52,7 +54,8 @@ protected:
void reportError(ScriptEnv *engine, bool fatal);
private:
ScriptEnv *m_engine;
QScriptEngine *m_engine;
ScriptEnv *m_env;
QScriptValue m_self;
};

View File

@ -37,19 +37,40 @@
#include "simplebindings/filedialogproxy.h"
#endif
ScriptEnv::ScriptEnv(QObject *parent)
: QScriptEngine(parent),
m_allowedUrls(NoUrls)
Q_DECLARE_METATYPE(ScriptEnv*)
ScriptEnv::ScriptEnv(QObject *parent, QScriptEngine *engine)
: QObject(parent),
m_allowedUrls(NoUrls),
m_engine(engine)
{
QScriptValue global = globalObject();
global.setProperty("print", newFunction(ScriptEnv::print));
global.setProperty("debug", newFunction(ScriptEnv::debug));
QScriptValue global = m_engine->globalObject();
// Add utility functions
global.setProperty("print", m_engine->newFunction(ScriptEnv::print));
global.setProperty("debug", m_engine->newFunction(ScriptEnv::debug));
// Add an accessor so we can find the scriptenv given only the engine. The
// property is hidden from scripts.
global.setProperty("__plasma_scriptenv", m_engine->newQObject(this),
QScriptValue::ReadOnly|QScriptValue::Undeletable|QScriptValue::SkipInEnumeration);
}
ScriptEnv::~ScriptEnv()
{
}
QScriptEngine *ScriptEnv::engine() const
{
return m_engine;
}
ScriptEnv *ScriptEnv::findScriptEnv( QScriptEngine *engine )
{
QScriptValue global = engine->globalObject();
return qscriptvalue_cast<ScriptEnv*>(global.property("__plasma_scriptenv"));
}
void ScriptEnv::registerEnums(QScriptValue &scriptValue, const QMetaObject &meta)
{
//manually create enum values. ugh
@ -58,7 +79,7 @@ void ScriptEnv::registerEnums(QScriptValue &scriptValue, const QMetaObject &meta
//kDebug() << e.name();
for (int i=0; i < e.keyCount(); ++i) {
//kDebug() << e.key(i) << e.value(i);
scriptValue.setProperty(e.key(i), QScriptValue(this, e.value(i)));
scriptValue.setProperty(e.key(i), QScriptValue(m_engine, e.value(i)));
}
}
}
@ -77,15 +98,15 @@ bool ScriptEnv::include(const QString &path)
// change the context to the parent context so that the include is actually
// executed in the same context as the caller; seems to be what javascript
// coders expect :)
QScriptContext *ctx = currentContext();
QScriptContext *ctx = m_engine->currentContext();
if (ctx && ctx->parentContext()) {
ctx->setActivationObject(ctx->parentContext()->activationObject());
ctx->setThisObject(ctx->parentContext()->thisObject());
}
evaluate(script, path);
m_engine->evaluate(script, path);
if (hasUncaughtException()) {
if (m_engine->hasUncaughtException()) {
emit reportError(this, true);
return false;
}
@ -122,7 +143,8 @@ QScriptValue ScriptEnv::runApplication(QScriptContext *context, QScriptEngine *e
QScriptValue ScriptEnv::runCommand(QScriptContext *context, QScriptEngine *engine)
{
Q_UNUSED(engine)
Q_UNUSED(engine);
if (context->argumentCount() == 0) {
return false;
}
@ -159,6 +181,7 @@ QScriptValue ScriptEnv::openUrl(QScriptContext *context, QScriptEngine *engine)
return false;
}
// TODO these should throw an exception
QScriptValue ScriptEnv::getUrl(QScriptContext *context, QScriptEngine *engine)
{
if (context->argumentCount() == 0) {
@ -172,12 +195,18 @@ QScriptValue ScriptEnv::getUrl(QScriptContext *context, QScriptEngine *engine)
return engine->undefinedValue();
}
ScriptEnv *env = ScriptEnv::findScriptEnv(engine);
if (!env) {
kDebug() << "findScriptEnv failed";
return engine->undefinedValue();
}
if (url.isLocalFile()) {
if (!(static_cast<ScriptEnv*>(engine)->m_allowedUrls & LocalUrls)) {
if (!(env->m_allowedUrls & LocalUrls)) {
return engine->undefinedValue();
}
} else if (!(static_cast<ScriptEnv*>(engine)->m_allowedUrls & NetworkUrls) &&
!((static_cast<ScriptEnv*>(engine)->m_allowedUrls & HttpUrls) && (url.protocol() == "http" || url.protocol() == "https"))) {
} else if (!(env->m_allowedUrls & NetworkUrls) &&
!((env->m_allowedUrls & HttpUrls) && (url.protocol() == "http" || url.protocol() == "https"))) {
return engine->undefinedValue();
}
@ -189,7 +218,7 @@ void ScriptEnv::registerGetUrl(QScriptValue &obj)
{
QScriptValue get = obj.property("getUrl");
if (!get.isValid()) {
obj.setProperty("getUrl", newFunction(ScriptEnv::getUrl));
obj.setProperty("getUrl", m_engine->newFunction(ScriptEnv::getUrl));
}
}
@ -198,13 +227,13 @@ bool ScriptEnv::importBuiltinExtension(const QString &extension, QScriptValue &o
kDebug() << extension;
if ("filedialog" == extension) {
#ifdef USEGUI
FileDialogProxy::registerWithRuntime(this);
FileDialogProxy::registerWithRuntime(m_engine);
return true;
#endif
} else if ("launchapp" == extension) {
obj.setProperty("runApplication", newFunction(ScriptEnv::runApplication));
obj.setProperty("runCommand", newFunction(ScriptEnv::runCommand));
obj.setProperty("openUrl", newFunction(ScriptEnv::openUrl));
obj.setProperty("runApplication", m_engine->newFunction(ScriptEnv::runApplication));
obj.setProperty("runCommand", m_engine->newFunction(ScriptEnv::runCommand));
obj.setProperty("openUrl", m_engine->newFunction(ScriptEnv::openUrl));
return true;
} else if ("http" == extension) {
m_allowedUrls |= HttpUrls;
@ -239,11 +268,11 @@ bool ScriptEnv::importExtensions(const KPluginInfo &info, QScriptValue &obj, Aut
if (!importBuiltinExtension(extension, obj)) {
if (auth.authorizeExternalExtensions()) {
importExtension(extension);
m_engine->importExtension(extension);
}
}
if (hasUncaughtException()) {
if (m_engine->hasUncaughtException()) {
emit reportError(this, true);
return false;
} else {
@ -266,11 +295,11 @@ bool ScriptEnv::importExtensions(const KPluginInfo &info, QScriptValue &obj, Aut
if (!importBuiltinExtension(extension, obj)) {
if (auth.authorizeExternalExtensions()) {
importExtension(extension);
m_engine->importExtension(extension);
}
}
if (hasUncaughtException()) {
if (m_engine->hasUncaughtException()) {
emit reportError(this, false);
} else {
m_extensions << extension;

View File

@ -27,7 +27,7 @@
#include "authorization.h"
class ScriptEnv : public QScriptEngine
class ScriptEnv : public QObject
{
Q_OBJECT
public:
@ -37,9 +37,15 @@ public:
LocalUrls = 4 };
Q_DECLARE_FLAGS(AllowedUrls, AllowedUrl)
ScriptEnv(QObject *parent);
ScriptEnv(QObject *parent, QScriptEngine *engine);
~ScriptEnv();
/** Returns the QScriptEngine in use. */
QScriptEngine *engine() const;
/** Returns the ScriptEnv in use for a given QScriptEngine or 0. */
static ScriptEnv *findScriptEnv( QScriptEngine *engine );
void registerEnums(QScriptValue &scriptValue, const QMetaObject &meta);
bool include(const QString &path);
@ -62,6 +68,7 @@ private:
QSet<QString> m_extensions;
AllowedUrls m_allowedUrls;
QScriptEngine *m_engine;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(ScriptEnv::AllowedUrls)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2007-2008 Richard J. Moore <rich@kde.org>
* Copyright 2007-2008,2010 Richard J. Moore <rich@kde.org>
* Copyright 2009 Aaron J. Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
@ -28,6 +28,7 @@
#include <QPauseAnimation>
#include <QSequentialAnimationGroup>
#include <QWidget>
#include <QScriptEngine>
#include <KConfigGroup>
#include <KDebug>
@ -87,11 +88,13 @@ QHash<QString, Plasma::Animator::Animation> SimpleJavaScriptApplet::s_animationD
SimpleJavaScriptApplet::SimpleJavaScriptApplet(QObject *parent, const QVariantList &args)
: Plasma::AppletScript(parent)
{
Q_UNUSED(args)
Q_UNUSED(args);
// kDebug() << "Script applet launched, args" << applet()->startupArguments();
m_engine = new ScriptEnv(this);
connect(m_engine, SIGNAL(reportError(ScriptEnv*,bool)), this, SLOT(engineReportsError(ScriptEnv*,bool)));
// TODO this will be set to the engine we get from QML
m_engine = new QScriptEngine(this);
m_env = new ScriptEnv(this, m_engine);
connect(m_env, SIGNAL(reportError(ScriptEnv*,bool)), this, SLOT(engineReportsError(ScriptEnv*,bool)));
}
SimpleJavaScriptApplet::~SimpleJavaScriptApplet()
@ -106,11 +109,11 @@ void SimpleJavaScriptApplet::engineReportsError(ScriptEnv *engine, bool fatal)
reportError(engine, fatal);
}
void SimpleJavaScriptApplet::reportError(QScriptEngine *engine, bool fatal)
void SimpleJavaScriptApplet::reportError(ScriptEnv *env, bool fatal)
{
SimpleJavaScriptApplet *jsApplet = qobject_cast<SimpleJavaScriptApplet *>(engine->parent());
AppletInterface *interface = extractAppletInterface(engine);
const QScriptValue error = engine->uncaughtException();
SimpleJavaScriptApplet *jsApplet = qobject_cast<SimpleJavaScriptApplet *>(env->parent());
AppletInterface *interface = extractAppletInterface(env->engine());
const QScriptValue error = env->engine()->uncaughtException();
QString file = error.property("fileName").toString();
if (interface) {
file.remove(interface->package()->path());
@ -129,7 +132,7 @@ void SimpleJavaScriptApplet::reportError(QScriptEngine *engine, bool fatal)
kDebug() << failureMsg;
}
kDebug() << engine->uncaughtExceptionBacktrace();
kDebug() << env->engine()->uncaughtExceptionBacktrace();
}
void SimpleJavaScriptApplet::configChanged()
@ -147,7 +150,7 @@ void SimpleJavaScriptApplet::configChanged()
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine);
reportError(m_env);
}
}
@ -168,7 +171,7 @@ void SimpleJavaScriptApplet::dataUpdated(const QString &name, const DataEngine::
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine);
reportError(m_env);
}
}
@ -190,7 +193,7 @@ void SimpleJavaScriptApplet::executeAction(const QString &name)
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine);
reportError(m_env);
}
}
}
@ -219,7 +222,7 @@ void SimpleJavaScriptApplet::paintInterface(QPainter *p, const QStyleOptionGraph
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine);
reportError(m_env);
}
}
@ -238,7 +241,7 @@ void SimpleJavaScriptApplet::callFunction(const QString &functionName, const QSc
m_engine->popContext();
if (m_engine->hasUncaughtException()) {
reportError(m_engine);
reportError(m_env);
}
}
}
@ -270,7 +273,7 @@ void SimpleJavaScriptApplet::constraintsEvent(Plasma::Constraints constraints)
bool SimpleJavaScriptApplet::include(const QString &path)
{
return m_engine->include(path);
return m_env->include(path);
}
void SimpleJavaScriptApplet::populateAnimationsHash()
@ -294,14 +297,14 @@ bool SimpleJavaScriptApplet::init()
setupObjects();
AppletAuthorization auth(this);
if (!m_engine->importExtensions(description(), m_self, auth)) {
if (!m_env->importExtensions(description(), m_self, auth)) {
return false;
}
kDebug() << "ScriptName:" << applet()->name();
kDebug() << "ScriptCategory:" << applet()->category();
return m_engine->include(mainScript());
return m_env->include(mainScript());
}
void SimpleJavaScriptApplet::setupObjects()
@ -334,7 +337,7 @@ void SimpleJavaScriptApplet::setupObjects()
}
global.setProperty("startupArguments", args);
m_engine->registerEnums(global, AppletInterface::staticMetaObject);
m_env->registerEnums(global, AppletInterface::staticMetaObject);
// Add a global loadui method for ui files
@ -379,7 +382,7 @@ void SimpleJavaScriptApplet::setupObjects()
QSet<QString> SimpleJavaScriptApplet::loadedExtensions() const
{
return m_engine->loadedExtensions();
return m_env->loadedExtensions();
}
AppletInterface *SimpleJavaScriptApplet::extractAppletInterface(QScriptEngine *engine)
@ -454,7 +457,7 @@ QScriptValue SimpleJavaScriptApplet::animation(QScriptContext *context, QScriptE
}
QScriptValue value = engine->newQObject(anim);
static_cast<ScriptEnv*>(engine)->registerEnums(value, *anim->metaObject());
ScriptEnv::findScriptEnv(engine)->registerEnums(value, *anim->metaObject());
return value;
}
}
@ -536,7 +539,7 @@ QScriptValue SimpleJavaScriptApplet::newPlasmaSvg(QScriptContext *context, QScri
Svg *svg = new Svg(parent);
svg->setImagePath(parentedToApplet ? findSvg(engine, filename) : filename);
QScriptValue fun = engine->newQObject(svg);
static_cast<ScriptEnv*>(engine)->registerEnums(fun, *svg->metaObject());
ScriptEnv::findScriptEnv(engine)->registerEnums(fun, *svg->metaObject());
return fun;
}
@ -555,7 +558,7 @@ QScriptValue SimpleJavaScriptApplet::newPlasmaFrameSvg(QScriptContext *context,
QScriptValue fun = engine->newQObject(frameSvg);
// FIXME: why is this necessary when it is clearly declared in FrameSvg's moc?
static_cast<ScriptEnv*>(engine)->registerEnums(fun, *frameSvg->metaObject());
ScriptEnv::findScriptEnv(engine)->registerEnums(fun, *frameSvg->metaObject());
return fun;
}
@ -577,7 +580,7 @@ QScriptValue SimpleJavaScriptApplet::newPlasmaExtenderItem(QScriptContext *conte
Plasma::ExtenderItem *extenderItem = new Plasma::ExtenderItem(extender);
QScriptValue fun = engine->newQObject(extenderItem);
static_cast<ScriptEnv*>(engine)->registerEnums(fun, *extenderItem->metaObject());
ScriptEnv::findScriptEnv(engine)->registerEnums(fun, *extenderItem->metaObject());
return fun;
}
@ -655,7 +658,7 @@ QScriptValue SimpleJavaScriptApplet::createWidget(QScriptContext *context, QScri
fun.setProperty("adjustSize", engine->newFunction(widgetAdjustSize));
//register enums will be accessed for instance as frame.Sunken for Frame shadow...
static_cast<ScriptEnv*>(engine)->registerEnums(fun, *w->metaObject());
ScriptEnv::findScriptEnv(engine)->registerEnums(fun, *w->metaObject());
return fun;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2007 Richard J. Moore <rich@kde.org>
* Copyright 2007, 2010 Richard J. Moore <rich@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
@ -28,6 +28,7 @@
#include "simplebindings/uiloader.h"
class QScriptContext;
class QScriptEngine;
class AppletInterface;
class ScriptEnv;
@ -46,7 +47,7 @@ public:
~SimpleJavaScriptApplet();
bool init();
static void reportError(QScriptEngine *error, bool fatal = false);
static void reportError(ScriptEnv *error, bool fatal = false);
void paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect);
QList<QAction*> contextualActions();
@ -102,7 +103,8 @@ private:
private:
static KSharedPtr<UiLoader> s_widgetLoader;
static QHash<QString, Plasma::Animator::Animation> s_animationDefs;
ScriptEnv *m_engine;
ScriptEnv *m_env;
QScriptEngine *m_engine;
QScriptValue m_self;
QVariantList m_args;
AppletInterface *m_interface;