From 25266b986c07cfdcfa4e5d48315641932ae09d16 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 7 Aug 2015 21:43:14 +0200 Subject: [PATCH] added basic implementation of WebSocket class ( https://developer.mozilla.org/en-US/docs/Web/API/WebSocket ) using QWebSocket --- interface/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 4 + .../script-engine/src/WebSocketClass.cpp | 82 ++++++++++++ libraries/script-engine/src/WebSocketClass.h | 118 ++++++++++++++++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 libraries/script-engine/src/WebSocketClass.cpp create mode 100644 libraries/script-engine/src/WebSocketClass.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f1ef38ade9..f1bcbf4c47 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -60,7 +60,7 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets) +find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 99d9149c3a..139b99e426 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME script-engine) # use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network Script WebSockets Widgets) add_dependency_external_projects(glm) find_package(GLM REQUIRED) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d5e727657c..afbf3eaf38 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,7 @@ #include "ScriptEngine.h" #include "TypedArrays.h" #include "XMLHttpRequestClass.h" +#include "WebSocketClass.h" #include "SceneScriptingInterface.h" @@ -344,6 +345,9 @@ void ScriptEngine::init() { QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); + globalObject().setProperty("WebSocket", webSocketConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp new file mode 100644 index 0000000000..1b8d305b16 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -0,0 +1,82 @@ +// +// WebSocketClass.cpp +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/4/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// This class is an implementation of the WebSocket object for scripting use. It provides a near-complete implementation +// of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptEngine.h" +#include "WebSocketClass.h" + +WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : + _engine(engine) +{ + connect(&_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); + connect(&_webSocket, SIGNAL(error()), this, SLOT(handleOnError())); + connect(&_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); + connect(&_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); + _webSocket.open(url); +} + +QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { + QString url; + QScriptValue callee = context->callee(); + if (context->argumentCount() == 1) { + url = context->argument(0).toString(); + } + return engine->newQObject(new WebSocketClass(engine, url)); +} + +WebSocketClass::~WebSocketClass() { + +} + +void WebSocketClass::send(QScriptValue message) { + _webSocket.sendTextMessage(message.toString()); +} + +void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { + _webSocket.close(closeCode, reason); +} + +void WebSocketClass::handleOnClose() { + if (_onCloseEvent.isFunction()) { + //QScriptValueList args; + //args << ("received: " + message); + _onCloseEvent.call();//(QScriptValue(), args); + } +} + +void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { + if (_onErrorEvent.isFunction()) { + // QScriptValueList args; + //args << ("received: " + message); + _onErrorEvent.call();/// QScriptValue(), args); + } +} + +void WebSocketClass::handleOnMessage(const QString& message) { + if (_onMessageEvent.isFunction()) { + + QScriptValueList args; + QScriptValue arg = _engine->newObject(); + arg.setProperty("data", message); + args << arg; + _onMessageEvent.call(QScriptValue(), args); + } +} + +void WebSocketClass::handleOnOpen() { + if (_onOpenEvent.isFunction()) { + //QScriptValueList args; + //args << ("received: " + message); + _onOpenEvent.call();// QScriptValue(), args); + } +} \ No newline at end of file diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h new file mode 100644 index 0000000000..6f0e3729c0 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.h @@ -0,0 +1,118 @@ +// +// WebSocketClass.h +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/4/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_WebSocketClass_h +#define hifi_WebSocketClass_h + +#include +#include +#include + +class WebSocketClass : public QObject { + Q_OBJECT + Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType) + Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount) + Q_PROPERTY(QString extensions READ getExtensions) + + Q_PROPERTY(QScriptValue onclose READ getOnClose WRITE setOnClose) + Q_PROPERTY(QScriptValue onerror READ getOnError WRITE setOnError) + Q_PROPERTY(QScriptValue onmessage READ getOnMessage WRITE setOnMessage) + Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen) + + Q_PROPERTY(QString protocol READ getProtocol) + Q_PROPERTY(uint readyState READ getReadyState) + Q_PROPERTY(QString url READ getURL) + + Q_PROPERTY(int CONNECTING READ getConnecting CONSTANT) + Q_PROPERTY(int OPEN READ getOpen CONSTANT) + Q_PROPERTY(int CLOSING READ getClosing CONSTANT) + Q_PROPERTY(int CLOSED READ getClosed CONSTANT) + +public: + WebSocketClass(QScriptEngine* engine, QString url); + ~WebSocketClass(); + + static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); + + enum ReadyState { + CONNECTING = 0, + OPEN, + CLOSING, + CLOSED + }; + + int getConnecting() const { return CONNECTING; }; + int getOpen() const { return OPEN; }; + int getClosing() const { return CLOSING; }; + int getClosed() const { return CLOSED; }; + + void setBinaryType(QString binaryType) { _binaryType = binaryType; } + QString getBinaryType() { return _binaryType; } + + // extensions is a empty string until supported in QT + QString getExtensions() { return QString(); } + + // protocol is a empty string until supported in QT + QString getProtocol() { return QString(); } + + //TODO: find buffered amount + ulong getBufferedAmount() { return 0; } + + QString getURL() { return _webSocket.requestUrl().toDisplayString(); } + + uint getReadyState() { + switch (_webSocket.state()) { + case QAbstractSocket::SocketState::HostLookupState: + case QAbstractSocket::SocketState::ConnectingState: + return CONNECTING; + case QAbstractSocket::SocketState::ConnectedState: + case QAbstractSocket::SocketState::BoundState: + case QAbstractSocket::SocketState::ListeningState: + return OPEN; + case QAbstractSocket::SocketState::ClosingState: + return CLOSING; + } + return CLOSED; + } + + void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; } + QScriptValue getOnClose() { return _onCloseEvent; } + void setOnError(QScriptValue eventFunction) { _onErrorEvent = eventFunction; } + QScriptValue getOnError() { return _onErrorEvent; } + void setOnMessage(QScriptValue eventFunction) { _onMessageEvent = eventFunction; } + QScriptValue getOnMessage() { return _onMessageEvent; } + void setOnOpen(QScriptValue eventFunction) { _onOpenEvent = eventFunction; } + QScriptValue getOnOpen() { return _onOpenEvent; } + +public slots: + void send(QScriptValue message); + void close(QWebSocketProtocol::CloseCode closeCode, QString reason); + +private: + QWebSocket _webSocket; + QScriptEngine* _engine; + + QScriptValue _onCloseEvent; + QScriptValue _onErrorEvent; + QScriptValue _onMessageEvent; + QScriptValue _onOpenEvent; + + QString _binaryType; + +private slots: + void handleOnClose(); + void handleOnError(QAbstractSocket::SocketError error); + void handleOnMessage(const QString& message); + void handleOnOpen(); + +}; + +#endif // hifi_WebSocketClass_h