summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorn Potter <lorn.potter@gmail.com>2018-09-24 17:25:08 +1000
committerEdward Welbourne <edward.welbourne@qt.io>2018-10-01 13:54:49 +0000
commit247373d6501bab94ce1ea0048e390bd910963db9 (patch)
tree3e29fc1458e1f74186ce488d6ceb1ab68c4767c6
parentdf6d430df70588238004bba0ca71b4d3beb7228c (diff)
downloadqtwebsockets-247373d6501bab94ce1ea0048e390bd910963db9.tar.gz
wasm: refactor and convert from EM_ASMv5.12.0-beta4v5.12.0-beta3v5.12.0-beta2v5.12.0-beta1
This also has the benefit that binary data is no longer accessed via a global. Change-Id: I2db744696fe88ca03f55897016d9d38e302f820b Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
-rw-r--r--src/websockets/qwebsocket_p.h7
-rw-r--r--src/websockets/qwebsocket_wasm_p.cpp224
2 files changed, 94 insertions, 137 deletions
diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h
index 73f2745..fd631d4 100644
--- a/src/websockets/qwebsocket_p.h
+++ b/src/websockets/qwebsocket_p.h
@@ -69,6 +69,10 @@
#include "qwebsocketdataprocessor_p.h"
#include "qdefaultmaskgenerator_p.h"
+#ifdef Q_OS_WASM
+#include <emscripten/val.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QWebSocketHandshakeRequest;
@@ -247,6 +251,9 @@ private:
QMap<QString, QString> m_headers;
friend class QWebSocketServerPrivate;
+#ifdef Q_OS_WASM
+ emscripten::val socketContext = emscripten::val::null();
+#endif
};
QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocket_wasm_p.cpp b/src/websockets/qwebsocket_wasm_p.cpp
index 7b9f271..2ac61ba 100644
--- a/src/websockets/qwebsocket_wasm_p.cpp
+++ b/src/websockets/qwebsocket_wasm_p.cpp
@@ -42,92 +42,104 @@
#include <QtCore/qcoreapplication.h>
#include <emscripten.h>
-#include <emscripten/html5.h>
#include <emscripten/bind.h>
using namespace emscripten;
-QByteArray g_messageArray;
-// easiest way to transliterate binary data to js/wasm
-
-val getBinaryMessage()
+static void q_onErrorCallback(val event)
{
- return val(typed_memory_view(g_messageArray.size(),
- reinterpret_cast<const unsigned char *>(g_messageArray.constData())));
-}
+ val target = event["target"];
-EMSCRIPTEN_BINDINGS(wasm_module) {
- function("getBinaryMessage", &getBinaryMessage);
+ QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
+ Q_ASSERT (wsp);
+
+ emit wsp->q_func()->error(wsp->error());
}
-static void onOpenCallback(void *data)
+static void q_onCloseCallback(val event)
{
- auto handler = reinterpret_cast<QWebSocketPrivate *>(data);
- Q_ASSERT (handler);
- emit handler->q_func()->connected();
+ val target = event["target"];
+
+ QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
+ Q_ASSERT (wsp);
+
+ emit wsp->q_func()->disconnected();
}
-static void onCloseCallback(void *data, int message)
+static void q_onOpenCallback(val event)
{
- Q_UNUSED(message);
- auto handler = reinterpret_cast<QWebSocketPrivate *>(data);
- Q_ASSERT (handler);
- emit handler->q_func()->disconnected();
+ val target = event["target"];
+
+ QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
+ Q_ASSERT (wsp);
+
+ emit wsp->q_func()->connected();
}
-static void onErrorCallback(void *data, int message)
+static void q_onIncomingMessageCallback(val event)
{
- Q_UNUSED(message);
- auto handler = reinterpret_cast<QWebSocketPrivate *>(data);
- Q_ASSERT (handler);
- emit handler->q_func()->error(handler->error());
+ val target = event["target"];
+
+ if (event["data"].typeOf().as<std::string>() == "string") {
+ QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
+ Q_ASSERT (wsp);
+
+ const QString message = QString::fromStdString(event["data"].as<std::string>());
+ if (!message.isEmpty())
+ wsp->q_func()->textMessageReceived(message);
+ } else {
+ val reader = val::global("FileReader").new_();
+ reader.set("onload", val::module_property("QWebSocketPrivate_readBlob"));
+ reader.set("data-context", target["data-context"]);
+ reader.call<void>("readAsArrayBuffer", event["data"]);
+ }
}
-static void onIncomingMessageCallback(void *data, int message, int length, int dataType)
+static void q_readBlob(val event)
{
- QWebSocketPrivate *handler = reinterpret_cast<QWebSocketPrivate *>(data);
- Q_ASSERT (handler);
-
- QWebSocket *webSocket = handler->q_func();
- const char *text = reinterpret_cast<const char *>(message);
-
- switch (dataType) {
- case 0: //string
- webSocket->textMessageReceived(QLatin1String(text));
- break;
- case 1: //blob
- case 2: //arraybuffer
- webSocket->binaryMessageReceived(QByteArray::fromRawData(text, length));
- break;
- };
+ val fileReader = event["target"];
+
+ QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(fileReader["data-context"].as<quintptr>());
+ Q_ASSERT (wsp);
+
+ // Set up source typed array
+ val result = fileReader["result"]; // ArrayBuffer
+ val Uint8Array = val::global("Uint8Array");
+ val sourceTypedArray = Uint8Array.new_(result);
+
+ // Allocate and set up destination typed array
+ const size_t size = result["byteLength"].as<size_t>();
+ QByteArray buffer(size, Qt::Uninitialized);
+
+ val destinationTypedArray = Uint8Array.new_(val::module_property("HEAPU8")["buffer"],
+ reinterpret_cast<quintptr>(buffer.data()), size);
+ destinationTypedArray.call<void>("set", sourceTypedArray);
+
+ wsp->q_func()->binaryMessageReceived(buffer);
+}
+
+
+EMSCRIPTEN_BINDINGS(wasm_module) {
+ function("QWebSocketPrivate_onErrorCallback", q_onErrorCallback);
+ function("QWebSocketPrivate_onCloseCallback", q_onCloseCallback);
+ function("QWebSocketPrivate_onOpenCallback", q_onOpenCallback);
+ function("QWebSocketPrivate_onIncomingMessageCallback", q_onIncomingMessageCallback);
+ function("QWebSocketPrivate_readBlob", q_readBlob);
}
qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
{
- EM_ASM_ARGS({
- if (window.qWebSocket === undefined)
- console.log("cannot find websocket object");
- else
- window.qWebSocket.send(Pointer_stringify($0));
- }, message.toLocal8Bit().constData());
-
+ socketContext.call<void>("send", message.toStdString());
return message.length();
}
qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
{
- g_messageArray = data;
- EM_ASM({
- if (window.qWebSocket === undefined) {
- console.log("cannot find websocket object");
- } else {
- var array = Module.getBinaryMessage();
- window.qWebSocket.binaryType = 'arraybuffer';
- window.qWebSocket.send(array);
- }
- });
-
- g_messageArray.clear();
+ socketContext.call<void>("send",
+ val(typed_memory_view(data.size(),
+ reinterpret_cast<const unsigned char *>
+ (data.constData()))));
+
return data.length();
}
@@ -136,19 +148,10 @@ void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString r
Q_Q(QWebSocket);
m_closeCode = closeCode;
m_closeReason = reason;
- const quint16 closeReason = (quint16)closeCode;
Q_EMIT q->aboutToClose();
- QCoreApplication::processEvents();
-
- EM_ASM_ARGS({
- if (window.qWebSocket === undefined) {
- console.log("cannot find websocket object");
- } else {
- var reasonMessage = Pointer_stringify($0);
- window.qWebSocket.close($1, reasonMessage);
- window.qWebSocket = undefined;
- }
- }, reason.toLatin1().data(), closeReason);
+
+ socketContext.call<void>("close", static_cast<quint16>(closeCode),
+ reason.toLatin1().toStdString());
}
void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask)
@@ -162,74 +165,21 @@ void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask)
return;
}
- QByteArray urlbytes = url.toString().toUtf8();
+ const std::string urlbytes = url.toString().toStdString();
// HTML WebSockets do not support arbitrary request headers, but
// do support the WebSocket protocol header. This header is
// required for some use cases like MQTT.
- QByteArray protocolHeaderValue = request.rawHeader("Sec-WebSocket-Protocol");
-
- EM_ASM_ARGS({
- if (window.qWebSocket != undefined)
- return;
-
- var wsUri = Pointer_stringify($0);
- var wsProtocol = Pointer_stringify($1);
- var handler = $2;
- var onOpenCb = $3;
- var onCloseCb = $4;
- var onErrorCb = $5;
- var onIncomingMessageCb = $6;
-
- window.qWebSocket = wsProtocol.length > 0
- ? new WebSocket(wsUri, wsProtocol)
- : new WebSocket(wsUri);
-
- window.qWebSocket.onopen = function(event) {
- Runtime.dynCall('vi', onOpenCb, [handler]);
- };
-
- window.qWebSocket.onclose = function(event) {
- window.qWebSocket = undefined;
- Runtime.dynCall('vii', onCloseCb, [handler, event.code]);
- };
-
- window.qWebSocket.onerror = function(event) {
- Runtime.dynCall('vii', onErrorCb, [handler, event.error]);
- };
-
- window.qWebSocket.onmessage = function(event) {
- var outgoingMessage;
- var bufferLength = 0;
- var dataType;
-
- if (window.qWebSocket.binaryType == 'arraybuffer' && typeof event.data == 'object') {
-
- var byteArray = new Uint8Array(event.data);
- bufferLength = byteArray.length;
-
- outgoingMessage = _malloc(byteArray.length);
- HEAPU8.set(byteArray, outgoingMessage);
-
- dataType = 2;
- } else if (typeof event.data == 'string') {
- dataType = 0;
- outgoingMessage = allocate(intArrayFromString(event.data), 'i8', ALLOC_NORMAL);
- } else if (window.qWebSocket.binaryType == 'blob') {
- var byteArray = new Int8Array($0);
- outgoingMessage = new Blob(byteArray.buffer);
- dataType = 1;
- }
-
- Runtime.dynCall('viiii', onIncomingMessageCb, [handler, outgoingMessage, bufferLength, dataType]);
- _free(outgoingMessage);
- };
-
- }, urlbytes.constData(),
- protocolHeaderValue.data(),
- this,
- reinterpret_cast<void *>(onOpenCallback),
- reinterpret_cast<void *>(onCloseCallback),
- reinterpret_cast<void *>(onErrorCallback),
- reinterpret_cast<void *>(onIncomingMessageCallback));
+ const std::string protocolHeaderValue = request.rawHeader("Sec-WebSocket-Protocol").toStdString();
+ val webSocket = val::global("WebSocket");
+
+ socketContext = !protocolHeaderValue.empty()
+ ? webSocket.new_(urlbytes, protocolHeaderValue)
+ : webSocket.new_(urlbytes);
+
+ socketContext.set("onerror", val::module_property("QWebSocketPrivate_onErrorCallback"));
+ socketContext.set("onclose", val::module_property("QWebSocketPrivate_onCloseCallback"));
+ socketContext.set("onopen", val::module_property("QWebSocketPrivate_onOpenCallback"));
+ socketContext.set("onmessage", val::module_property("QWebSocketPrivate_onIncomingMessageCallback"));
+ socketContext.set("data-context", val(quintptr(reinterpret_cast<void *>(this))));
}