morten.sorvig@qt.io
Qt for Web Browsers
Introduction to WebAssembly and Emscripten
Building for WebAssembly
Qt applications as a <canvas>
Local File Access
Networking
JavaScript and C++ Interop
(The story so far)
Qt for Google Native Client
Qt WebGL Streaming
Qt for WebAssembly
Build once, deploy everywhere
No installation required
Security through sandboxing
New platform for 5.12: Q_OS_WASM
Tech Preview
Host system support: GNU/Linux, macOS, Windows Subsystem for Linux
$ ~/Qt/5.12.0/Src/configure -xplatform wasm-emscripten -nomake examples
$ make module-qtbase module-qtdeclarative module-qtquickcontrols2
Implicit configure flags: -static -no-feature-thread
Suppored Modules: QtBase, QtDeclarative, QQuickControls2, QtCharts, QtWebSockets, QtMQTT ++
Not Supported: QtMultimedia, QtWebEngine
No system cliboard support
No virtual keyboard support on mobile
No nested event loop support (QEventLoop::exec())
No system font support (bring your own fonts)
No accessibility/screen reader support
$ /path/to/qt-wasm/qtbase/bin/qmake
$ make
Will produce .wasm, .js and .html files
These can be served from any web server
Name | Source | Purpose |
---|---|---|
app.html | Qt | HTML container |
qtloader.js | Qt | JS API for loading Qt apps |
app.js | emscripten | app runtime and JS API |
app.wasm | emscripten | app binary |
(compressed)
Modules | gzip | brotli |
---|---|---|
Core Gui | 2.8MB | 2.1MB |
Core Gui Widgets | 4.3MB | 3.2MB |
Core Gui Widgets Quick Charts | 8.6MB | 6.3MB |
The application is embedded in the html page as a canvas element
<div>
<canvas>
<!--- App Appears Here -->
</canvas>
</div>
The html file controls cavas position and size
The application sees a QScreen with geometry matching that of the canvas
Image editor made with Qt Quick Controls 2
Sandbox prevents direct file system access
Emscripten creates an in-memory file system, accessible by QFile
HTML has API for opening a file dialog, and starting a file download
Not Compatible with QFileDialog - New API is needed
Callback-based loadFile()
QWasmFile::loadFile("*.txt",
[](const QByteArray &fileContent,
const QString &fileName) {
qDebug() << "Got" << fileContent.count()
<< "bytes from" << fileName;
});
Fire-and-forget saveFile()
QByteArray content = ...;
QString fileNameHint = ...;
QWasmFile::saveFile(content, fileNameHint);
API | Protocol | Notes |
---|---|---|
QNetworkAccessManager | Http | Same Origin / CORS host |
QWebSocket | WebSocket | Any host |
QAbstractSocket | WebSocket | Websockify forwarding host |
Use case: The applicaiton is a web page component
We want bidirectional transfer of control and state
Emscripten provides API for JavaScript <-> C++ interop
Using API from val.h
using emscripten::val;
val document = val::global("document");
val input =
document.call<val>("createElement", std::string("input"));
val body = document["body"];
body.call<void>("appendChild", input);
Export C++ API to JavaScript
#include <emscripten/bind.h>
void setColor(int r, int g, int b, int colorSpaceIndex);
EMSCRIPTEN_BINDINGS(colorDebugger) {
emscripten::function("setColor", &setColor);
}
Make call from JavaScript
Module.setColor(rgb[0], rgb[1], rgb[2], colorSpaceIndex);