summaryrefslogtreecommitdiff
path: root/daemon/jskitmanager.cpp
diff options
context:
space:
mode:
authorTomasz Sterna <tomek@xiaoka.com>2015-01-03 16:38:02 +0100
committerTomasz Sterna <tomek@xiaoka.com>2015-01-03 19:07:48 +0100
commit4e7da1944f5fa75a0739c0757d40a8102f045365 (patch)
tree5f3fe179256536e4135eb4d5031a1d754af5e26c /daemon/jskitmanager.cpp
parent4150005566bec7827ce1cdd759a2397d47eba583 (diff)
parente6ec758b364fcaf9fda35e56740c3fcd7e8fe25e (diff)
Merge remote-tracking branch 'javispedro/js-testing'
Conflicts: daemon/daemon.pro daemon/dbusconnector.cpp daemon/manager.cpp daemon/watchcommands.cpp daemon/watchcommands.h daemon/watchconnector.cpp daemon/watchconnector.h log4qt-debug.conf log4qt-release.conf rpm/pebble.spec rpm/pebble.yaml
Diffstat (limited to 'daemon/jskitmanager.cpp')
-rw-r--r--daemon/jskitmanager.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp
new file mode 100644
index 0000000..f6a3f24
--- /dev/null
+++ b/daemon/jskitmanager.cpp
@@ -0,0 +1,205 @@
+#include <QFile>
+#include <QDir>
+
+#include "jskitmanager.h"
+#include "jskitobjects.h"
+
+JSKitManager::JSKitManager(WatchConnector *watch, AppManager *apps, AppMsgManager *appmsg, Settings *settings, QObject *parent) :
+ QObject(parent), l(metaObject()->className()),
+ _watch(watch), _apps(apps), _appmsg(appmsg), _settings(settings), _engine(0)
+{
+ connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
+ connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
+}
+
+JSKitManager::~JSKitManager()
+{
+ if (_engine) {
+ stopJsApp();
+ }
+}
+
+QJSEngine * JSKitManager::engine()
+{
+ return _engine;
+}
+
+bool JSKitManager::isJSKitAppRunning() const
+{
+ return _engine != 0;
+}
+
+QString JSKitManager::describeError(QJSValue error)
+{
+ return QString("%1:%2: %3")
+ .arg(error.property("fileName").toString())
+ .arg(error.property("lineNumber").toInt())
+ .arg(error.toString());
+}
+
+void JSKitManager::showConfiguration()
+{
+ if (_engine) {
+ qCDebug(l) << "requesting configuration";
+ _jspebble->invokeCallbacks("showConfiguration");
+ } else {
+ qCWarning(l) << "requested to show configuration, but JS engine is not running";
+ }
+}
+
+void JSKitManager::handleWebviewClosed(const QString &result)
+{
+ if (_engine) {
+ QJSValue eventObj = _engine->newObject();
+ eventObj.setProperty("response", _engine->toScriptValue(result));
+
+ qCDebug(l) << "webview closed with the following result: " << result;
+
+ _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj}));
+ } else {
+ qCWarning(l) << "webview closed event, but JS engine is not running";
+ }
+}
+
+void JSKitManager::handleAppStarted(const QUuid &uuid)
+{
+ AppInfo info = _apps->info(uuid);
+ if (!info.uuid().isNull() && info.isJSKit()) {
+ qCDebug(l) << "Preparing to start JSKit app" << info.uuid() << info.shortName();
+ _curApp = info;
+ startJsApp();
+ }
+}
+
+void JSKitManager::handleAppStopped(const QUuid &uuid)
+{
+ if (!_curApp.uuid().isNull()) {
+ if (_curApp.uuid() != uuid) {
+ qCWarning(l) << "Closed app with invalid UUID";
+ }
+
+ stopJsApp();
+ _curApp.setUuid(QUuid()); // Clear the uuid to force invalid app
+ }
+}
+
+void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg)
+{
+ if (_curApp.uuid() == uuid) {
+ qCDebug(l) << "received a message for the current JSKit app";
+
+ if (!_engine) {
+ qCDebug(l) << "but engine is stopped";
+ return;
+ }
+
+ QJSValue eventObj = _engine->newObject();
+ eventObj.setProperty("payload", _engine->toScriptValue(msg));
+
+ _jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj}));
+ }
+}
+
+bool JSKitManager::loadJsFile(const QString &filename)
+{
+ Q_ASSERT(_engine);
+
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qCWarning(l) << "Failed to load JS file:" << file.fileName();
+ return false;
+ }
+
+ qCDebug(l) << "now parsing" << file.fileName();
+
+ QJSValue result = _engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
+ if (result.isError()) {
+ qCWarning(l) << "error while evaluating JS script:" << describeError(result);
+ return false;
+ }
+
+ qCDebug(l) << "JS script evaluated";
+
+ return true;
+}
+
+void JSKitManager::startJsApp()
+{
+ if (_engine) stopJsApp();
+ if (_curApp.uuid().isNull()) {
+ qCWarning(l) << "Attempting to start JS app with invalid UUID";
+ return;
+ }
+
+ _engine = new QJSEngine(this);
+ _jspebble = new JSKitPebble(_curApp, this);
+ _jsconsole = new JSKitConsole(this);
+ _jsstorage = new JSKitLocalStorage(_curApp.uuid(), this);
+ _jsgeo = new JSKitGeolocation(this);
+
+ qCDebug(l) << "starting JS app";
+
+ QJSValue globalObj = _engine->globalObject();
+
+ globalObj.setProperty("Pebble", _engine->newQObject(_jspebble));
+ globalObj.setProperty("console", _engine->newQObject(_jsconsole));
+ globalObj.setProperty("localStorage", _engine->newQObject(_jsstorage));
+
+ QJSValue navigatorObj = _engine->newObject();
+ navigatorObj.setProperty("geolocation", _engine->newQObject(_jsgeo));
+ navigatorObj.setProperty("language", _engine->toScriptValue(QLocale().name()));
+ globalObj.setProperty("navigator", navigatorObj);
+
+ // Set this.window = this
+ globalObj.setProperty("window", globalObj);
+
+ // Shims for compatibility...
+ QJSValue result = _engine->evaluate(
+ "function XMLHttpRequest() { return Pebble.createXMLHttpRequest(); }\n"
+ );
+ Q_ASSERT(!result.isError());
+
+ // Polyfills...
+ loadJsFile("/usr/share/pebble/js/typedarray.js");
+
+ // Now load the actual script
+ loadJsFile(_curApp.path() + "/pebble-js-app.js");
+
+ // Setup the message callback
+ QUuid uuid = _curApp.uuid();
+ _appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) {
+ QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection,
+ Q_ARG(QUuid, uuid),
+ Q_ARG(QVariantMap, msg));
+
+ // Invoke the slot as a queued connection to give time for the ACK message
+ // to go through first.
+
+ return true;
+ });
+
+ // We try to invoke the callbacks even if script parsing resulted in error...
+ _jspebble->invokeCallbacks("ready");
+}
+
+void JSKitManager::stopJsApp()
+{
+ if (!_engine) return; // Nothing to do!
+
+ qCDebug(l) << "stopping JS app";
+
+ if (!_curApp.uuid().isNull()) {
+ _appmsg->clearMessageHandler(_curApp.uuid());
+ }
+
+ _engine->collectGarbage();
+
+ _engine->deleteLater();
+ _engine = 0;
+ _jsstorage->deleteLater();
+ _jsstorage = 0;
+ _jsgeo->deleteLater();
+ _jsgeo = 0;
+ _jspebble->deleteLater();
+ _jspebble = 0;
+}