summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKent Hansen <kent.hansen@nokia.com>2011-03-09 23:44:59 +0100
committerKent Hansen <kent.hansen@nokia.com>2011-03-10 11:53:53 +0100
commit1d1094ddcb35e1148deb6e8da470dcf0d314a3f7 (patch)
tree0fb48163f57d4d2c1928f255e45c00b465e99e3d
Initialize repository
-rw-r--r--README34
-rw-r--r--examples/README3
-rw-r--r--examples/console.js3
-rw-r--r--examples/document.js5
-rw-r--r--examples/import-envjsnatives.js6
-rw-r--r--examples/prototype-addclass.js8
-rw-r--r--examples/window.js3
-rw-r--r--examples/xmlhttprequest.js9
-rw-r--r--src/plugin/plugin.cpp44
-rw-r--r--src/plugin/plugin.pro8
-rw-r--r--src/shared/envjsnatives.cpp275
-rw-r--r--src/src.pro2
12 files changed, 400 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..2a7e60a
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+Envjs implementation for QtScript.
+
+Envjs (http://www.envjs.com) is a simulated browser environment written in JavaScript.
+
+The code in this repository only implements the Envjs parts that need platform support.
+You'll also need a file called env.js (not included in this repository), which is the
+main Envjs implementation. To create it:
+1. Go to http://www.envjs.com/release/envjs-1.2 and download the full release.
+2. Unpack.
+3. Run ant from the base Envjs directory.
+4. Presto, dist/env.js is ready for evaluation.
+
+To use Envjs with QtScript, you'll need to
+1. Evaluate env.js.
+2. Call the function initializeEnvjsNatives() (in src/shared/envjsnatives.cpp).
+
+To achieve that, there are two major options:
+
+1. Fully embed Envjs in the application: Pass the contents of env.js to
+QScriptEngine::evaluate(), and then call initializeEnvjsNatives(). You'll need
+to add src/shared/envjsnatives.cpp to your application sources.
+
+2. Build the envjsnatives plugin (src/plugin) and put it in e.g. $QTDIR/plugins/script.
+Then QScriptEngine::importExtension("envjs") can be used to load the native implementation.
+env.js still needs to be evaluated separately before the plugin is loaded, otherwise
+the plugin will throw an error.
+
+API-specific notes:
+- In order for asynchronous XMLHttpRequest requests to work, you need to be running an
+event loop.
+
+Tested with Envjs version 1.2.
+
+See also examples/README.
diff --git a/examples/README b/examples/README
new file mode 100644
index 0000000..c56027c
--- /dev/null
+++ b/examples/README
@@ -0,0 +1,3 @@
+To run the document.js example with $QTDIR/examples/script/qscript:
+- Make sure envjsnatives plugin is installed (in e.g. $QTDIR/plugins/script)
+- Run qscript path/to/env.js import-envjsnatives.js document.js
diff --git a/examples/console.js b/examples/console.js
new file mode 100644
index 0000000..b170afe
--- /dev/null
+++ b/examples/console.js
@@ -0,0 +1,3 @@
+console.log("Log output");
+console.warn("Warn output");
+console.error("Error output");
diff --git a/examples/document.js b/examples/document.js
new file mode 100644
index 0000000..b0c2496
--- /dev/null
+++ b/examples/document.js
@@ -0,0 +1,5 @@
+console.log("document:", document);
+document.write("<p>Hello, JavaScript in QtScript!</p>");
+document.close();
+console.log("p tag's innerHTML:", document.getElementsByTagName("p").item(0).innerHTML);
+console.log("document.innerHTML:", document.innerHTML);
diff --git a/examples/import-envjsnatives.js b/examples/import-envjsnatives.js
new file mode 100644
index 0000000..f6bff3c
--- /dev/null
+++ b/examples/import-envjsnatives.js
@@ -0,0 +1,6 @@
+// This is not an actual example, it's just a helper
+// when using e.g. $QTDIR/examples/script/qscript to
+// run examples.
+// Evaluate this file after evaluating env.js
+// to establish the full Envjs/QtScript environment.
+qt.script.importExtension("envjsnatives");
diff --git a/examples/prototype-addclass.js b/examples/prototype-addclass.js
new file mode 100644
index 0000000..a7ac196
--- /dev/null
+++ b/examples/prototype-addclass.js
@@ -0,0 +1,8 @@
+// Assumes prototype.js has been evaluated.
+
+document.close();
+
+var my_div = document.createElement('div');
+my_div.addClassName('prototyped').hide();
+document.body.appendChild(my_div);
+console.log("document.innerHTML:", document.innerHTML);
diff --git a/examples/window.js b/examples/window.js
new file mode 100644
index 0000000..e7c1e1b
--- /dev/null
+++ b/examples/window.js
@@ -0,0 +1,3 @@
+console.log("window:", window);
+console.log("window === this?", window === this);
+console.log("window.navigator.userAgent:", window.navigator.userAgent);
diff --git a/examples/xmlhttprequest.js b/examples/xmlhttprequest.js
new file mode 100644
index 0000000..3afddc9
--- /dev/null
+++ b/examples/xmlhttprequest.js
@@ -0,0 +1,9 @@
+req = new XMLHttpRequest();
+req.onreadystatechange = function() {
+ print("onreadystatechange, state ==", req.readyState);
+ if (req.readyState == 4) {
+ console.log(req.responseText);
+ }
+};
+req.open("GET", "http://labs.qt.nokia.com", false);
+req.send(null);
diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp
new file mode 100644
index 0000000..687cad8
--- /dev/null
+++ b/src/plugin/plugin.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file may be used under the terms of the GNU General Public
+** License version 2.0 or 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of
+** this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtScript>
+
+extern void initializeEnvjsNatives(QScriptEngine *);
+
+class EnvjsPlugin : public QScriptExtensionPlugin
+{
+public:
+ QStringList keys() const;
+ void initialize(const QString &key, QScriptEngine *);
+};
+
+QStringList EnvjsPlugin::keys() const
+{
+ return QStringList() << "envjsnatives";
+}
+
+void EnvjsPlugin::initialize(const QString &/* key */, QScriptEngine *eng)
+{
+ initializeEnvjsNatives(eng);
+}
+
+Q_EXPORT_PLUGIN2(envjsplugin, EnvjsPlugin)
diff --git a/src/plugin/plugin.pro b/src/plugin/plugin.pro
new file mode 100644
index 0000000..4a3f4b2
--- /dev/null
+++ b/src/plugin/plugin.pro
@@ -0,0 +1,8 @@
+TEMPLATE = lib
+TARGET = envjsnatives
+QT = core script network
+CONFIG += plugin
+INCLUDEPATH += .
+
+SOURCES += plugin.cpp
+SOURCES += ../shared/envjsnatives.cpp
diff --git a/src/shared/envjsnatives.cpp b/src/shared/envjsnatives.cpp
new file mode 100644
index 0000000..6508f20
--- /dev/null
+++ b/src/shared/envjsnatives.cpp
@@ -0,0 +1,275 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file may be used under the terms of the GNU General Public
+** License version 2.0 or 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of
+** this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtScript>
+#include <QtNetwork>
+
+// Exported API.
+void initializeEnvjsNatives(QScriptEngine *);
+
+#define ENVJS_FUNCTIONS(V) \
+ V(lineSource) \
+ V(eval) \
+ V(loadInlineScript) \
+ V(loadLocalScript) \
+ V(sync) \
+ V(spawn) \
+ V(sleep) \
+ V(loadFrame) \
+ V(unloadFrame) \
+ V(proxy) \
+ V(getcwd) \
+ V(runAsync) \
+ V(writeToFile) \
+ V(writeToTempFile) \
+ V(readFromFile) \
+ V(deleteFile) \
+ V(connection)
+
+static QScriptValue Envjs_lineSource(QScriptContext *, QScriptEngine *)
+{
+ // Supposed to return the source text of the line causing the error.
+ //QScriptValue e = ctx->argument(0);
+ return "(line ?)";
+}
+
+static QScriptValue Envjs_eval(QScriptContext *ctx, QScriptEngine *eng)
+{
+ // Don't know what this is. this-object? Variable scope?
+ // QScriptValue scope = ctx->argument(0);
+
+ QScriptValue source = ctx->argument(1);
+ QScriptValue sourceName = ctx->argument(0);
+ return eng->evaluate(source.toString(), sourceName.toString());
+}
+
+static QScriptValue Envjs_loadInlineScript(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->throwError("loadInlineScript() not implemented");
+}
+
+static QScriptValue Envjs_loadLocalScript(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->throwError("loadLocalScript() not implemented");
+}
+
+static QScriptValue Envjs_sync(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->argument(0);
+}
+
+static QScriptValue Envjs_spawn(QScriptContext *ctx, QScriptEngine *)
+{
+ QScriptValue fun = ctx->argument(0);
+ return fun.call();
+}
+
+class SleepyThread : public QThread
+{
+public:
+ static void msleep(unsigned long msecs)
+ { QThread::msleep(msecs); }
+};
+
+static QScriptValue Envjs_sleep(QScriptContext *ctx, QScriptEngine *eng)
+{
+ uint msecs = ctx->argument(0).toUInt32();
+ static_cast<SleepyThread*>(QThread::currentThread())->msleep(msecs);
+ return eng->undefinedValue();
+}
+
+static QScriptValue Envjs_loadFrame(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->throwError("loadFrame() not implemented");
+}
+
+static QScriptValue Envjs_unloadFrame(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->throwError("unloadFrame() not implemented");
+}
+
+static QScriptValue Envjs_proxy(QScriptContext *ctx, QScriptEngine *)
+{
+ return ctx->throwError("proxy() not implemented");
+}
+
+static QScriptValue Envjs_getcwd(QScriptContext *, QScriptEngine *)
+{
+ return QDir::currentPath();
+}
+
+class QtScriptFunctionInvoker : public QObject
+{
+ Q_OBJECT
+public:
+ QtScriptFunctionInvoker(const QScriptValue &fn, QObject *parent = 0)
+ : QObject(parent), fun(fn)
+ {}
+
+ void invokeLater()
+ {
+ QMetaObject::invokeMethod(this, "invoke", Qt::QueuedConnection);
+ }
+
+private slots:
+ void invoke()
+ {
+ fun.call();
+ deleteLater();
+ }
+
+private:
+ QScriptValue fun;
+};
+
+static QScriptValue Envjs_runAsync(QScriptContext *ctx, QScriptEngine *eng)
+{
+ QScriptValue fun = ctx->argument(0);
+ QtScriptFunctionInvoker *invoker = new QtScriptFunctionInvoker(fun, eng);
+ invoker->invokeLater();
+ return eng->undefinedValue();
+}
+
+static QScriptValue Envjs_writeToFile(QScriptContext *ctx, QScriptEngine *eng)
+{
+ QScriptValue text = ctx->argument(0);
+ QScriptValue url = ctx->argument(1);
+ QString path = QUrl(url.toString()).toLocalFile();
+ QFile file(path);
+ file.open(QIODevice::WriteOnly);
+ QTextStream stream(&file);
+ stream.setCodec(QTextCodec::codecForName("UTF-8"));
+ stream << text.toString();
+ return eng->undefinedValue();
+}
+
+static QScriptValue Envjs_writeToTempFile(QScriptContext *ctx, QScriptEngine *)
+{
+// QScriptValue text = ctx->argument(0);
+// QScriptValue suffix = ctx->argument(1);
+// QString fileName = QString::fromLatin1("envjs-tmp%0").arg(suffix.toString()));
+// ### Create file and write text. File should be deleted when engine is.
+ return ctx->throwError("writeToTempFile() not implemented");
+}
+
+static QScriptValue Envjs_readFromFile(QScriptContext *ctx, QScriptEngine *)
+{
+ QScriptValue url = ctx->argument(0);
+ QString path = QUrl(url.toString()).toLocalFile();
+ QFile file(path);
+ file.open(QIODevice::ReadOnly);
+ QTextStream stream(&file);
+ stream.setCodec(QTextCodec::codecForName("UTF-8"));
+ return stream.readAll();
+}
+
+static QScriptValue Envjs_deleteFile(QScriptContext *ctx, QScriptEngine *eng)
+{
+ QScriptValue url = ctx->argument(0);
+ QString path = QUrl(url.toString()).toLocalFile();
+ QFile file(path);
+ file.remove();
+ return eng->undefinedValue();
+}
+
+static QScriptValue Envjs_connection(QScriptContext *ctx, QScriptEngine *eng)
+{
+ // TODO: if xhr.async is true, all this stuff should be done in a
+ // delayed call or different thread, then the response handler can
+ // be called (in this thread).
+
+ QScriptValue xhr = ctx->argument(0);
+ QScriptValue responseHandler = ctx->argument(1);
+ QScriptValue data = ctx->argument(2);
+ QUrl url = QUrl(xhr.property("url").toString());
+ QNetworkAccessManager nam; // TODO: get a manager from somewhere...
+ QNetworkRequest req(url);
+
+ QScriptValueIterator it(xhr.property("headers"));
+ while (it.hasNext()) {
+ it.next();
+ req.setRawHeader(it.name().toUtf8(), it.value().toString().toUtf8());
+ }
+
+ QString method = xhr.property("method").toString();
+ QNetworkReply *rep = 0;
+ if (method == QLatin1String("GET"))
+ rep = nam.get(req);
+ else if (method == QLatin1String("PUT"))
+ rep = nam.put(req, data.toString().toUtf8());
+ else if (method == QLatin1String("POST"))
+ rep = nam.put(req, data.toString().toUtf8());
+ else if (method == QLatin1String("HEAD"))
+ rep = nam.head(req);
+ else if (method == QLatin1String("DELETE"))
+ rep = nam.deleteResource(req);
+ else
+ return ctx->throwError(QString::fromLatin1("request method %0 not implemented").arg(method));
+ // TODO: connect to reply's download/uploadProgress(),
+ // implement HEADERS_RECEIVED and LOADING states
+
+ QEventLoop loop;
+ QObject::connect(rep, SIGNAL(finished()), &loop, SLOT(quit()));
+ loop.exec();
+
+ QScriptValue responseHeaders = xhr.property("responseHeaders");
+ QList<QNetworkReply::RawHeaderPair> rawHeaderPairs = rep->rawHeaderPairs();
+ for (int i = 0; i < rawHeaderPairs.size(); ++i) {
+ const QNetworkReply::RawHeaderPair &rhp = rawHeaderPairs.at(i);
+ responseHeaders.setProperty(QString::fromUtf8(rhp.first), QString::fromUtf8(rhp.second));
+ }
+
+ xhr.setProperty("readyState", 4);
+ QVariant statusCode = rep->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+ if (statusCode.isValid())
+ xhr.setProperty("status", statusCode.toInt());
+ QVariant reasonPhrase = rep->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
+ if (reasonPhrase.isValid())
+ xhr.setProperty("statusText", reasonPhrase.toString());
+
+ QByteArray responseData = rep->readAll();
+ xhr.setProperty("responseText", QString::fromUtf8(responseData));
+
+ if (responseHandler.isFunction())
+ responseHandler.call();
+
+ return eng->undefinedValue();
+}
+
+void initializeEnvjsNatives(QScriptEngine *eng)
+{
+ QScriptValue envjs = eng->globalObject().property("Envjs");
+ if (!envjs.isObject()) {
+ eng->currentContext()->throwError("env.js must be evaluated before natives can be initialized");
+ return;
+ }
+
+ envjs.setProperty("platform", "QtScript");
+
+ envjs.setProperty("log", eng->globalObject().property("print"));
+
+#define SET_ENVJS_FUNCTION(f) envjs.setProperty(#f, eng->newFunction(Envjs_##f));
+ ENVJS_FUNCTIONS(SET_ENVJS_FUNCTION)
+#undef SET_ENVJS_FUNCTION
+}
+
+#include "envjsnatives.moc"
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..e67a15e
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS = plugin