Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QSTDWEB_P_H
#define QSTDWEB_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qglobal_p.h>
#include <QtCore/qglobal.h>
#include "QtCore/qhash.h"
#include "QtCore/qiodevice.h"
#include "QtCore/private/qwasmsuspendresumecontrol_p.h"
#include <emscripten/val.h>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <utility>
#if QT_CONFIG(thread)
#include <emscripten/proxying.h>
#include <emscripten/threading.h>
#endif // #if QT_CONFIG(thread)
#if QT_CONFIG(wasm_jspi)
# define QT_WASM_EMSCRIPTEN_ASYNC ,emscripten::async()
#else
# define QT_WASM_EMSCRIPTEN_ASYNC
#endif
QT_BEGIN_NAMESPACE
class QMimeData;
namespace qstdweb {
extern const char makeContextfulPromiseFunctionName[];
// DOM API in C++, implemented using emscripten val.h and bind.h.
// This is private API and can be extended and changed as needed.
// The API mirrors that of the native API, with some extensions
// to ease usage from C++ code.
class ArrayBuffer;
class Blob;
class File;
class FileList;
class FileReader;
class Uint8Array;
class EventCallback;
class Q_CORE_EXPORT ArrayBuffer {
public:
explicit ArrayBuffer(uint32_t size);
explicit ArrayBuffer(const emscripten::val &arrayBuffer);
uint32_t byteLength() const;
ArrayBuffer slice(uint32_t begin, uint32_t end) const;
emscripten::val val() const;
private:
friend class Uint8Array;
emscripten::val m_arrayBuffer = emscripten::val::undefined();
};
class Q_CORE_EXPORT Blob {
public:
explicit Blob(const emscripten::val &blob);
static Blob fromArrayBuffer(const ArrayBuffer &arrayBuffer);
uint32_t size() const;
static Blob copyFrom(const char *buffer, uint32_t size, std::string mimeType);
static Blob copyFrom(const char *buffer, uint32_t size);
Blob slice(uint32_t begin, uint32_t end) const;
ArrayBuffer arrayBuffer_sync() const;
emscripten::val val() const;
std::string type() const;
private:
friend class FileReader;
emscripten::val m_blob = emscripten::val::undefined();
};
class Q_CORE_EXPORT File {
public:
File() = default;
explicit File(const emscripten::val &file);
~File();
File(const File &other);
File(File &&other);
File &operator=(const File &other);
File &operator=(File &&other);
Blob slice(uint64_t begin, uint64_t end) const;
std::string name() const;
uint64_t size() const;
std::string type() const;
void stream(uint32_t offset, uint32_t length, char *buffer,
std::function<void()> completed) const;
void stream(char *buffer, std::function<void()> completed) const;
emscripten::val val() const;
void fileUrlRegistration() const;
const QString &fileUrlPath() const { return m_urlPath; }
emscripten::val file() const { return m_file; }
private:
emscripten::val m_file = emscripten::val::undefined();
QString m_urlPath;
};
class Q_CORE_EXPORT FileUrlRegistration
{
public:
explicit FileUrlRegistration(File file);
~FileUrlRegistration();
FileUrlRegistration(const FileUrlRegistration &other) = delete;
FileUrlRegistration(FileUrlRegistration &&other);
FileUrlRegistration &operator=(const FileUrlRegistration &other) = delete;
FileUrlRegistration &operator=(FileUrlRegistration &&other);
const QString &path() const { return m_path; }
private:
QString m_path;
};
using FileUrlRegistrations = std::vector<std::unique_ptr<FileUrlRegistration>>;
class Q_CORE_EXPORT FileList {
public:
FileList() = default;
explicit FileList(const emscripten::val &fileList);
int length() const;
File item(int index) const;
File operator[](int index) const;
emscripten::val val() const;
private:
emscripten::val m_fileList = emscripten::val::undefined();
};
class Q_CORE_EXPORT FileReader {
public:
ArrayBuffer result() const;
void readAsArrayBuffer(const Blob &blob) const;
void onLoad(const std::function<void(emscripten::val)> &onLoad);
void onError(const std::function<void(emscripten::val)> &onError);
void onAbort(const std::function<void(emscripten::val)> &onAbort);
emscripten::val val() const;
private:
emscripten::val m_fileReader = emscripten::val::global("FileReader").new_();
std::unique_ptr<EventCallback> m_onLoad;
std::unique_ptr<EventCallback> m_onError;
std::unique_ptr<EventCallback> m_onAbort;
};
class Q_CORE_EXPORT Uint8Array {
public:
explicit Uint8Array(const emscripten::val &uint8Array);
explicit Uint8Array(const ArrayBuffer &buffer);
explicit Uint8Array(uint32_t size);
Uint8Array(const ArrayBuffer &buffer, uint32_t offset, uint32_t length);
Uint8Array(const char *buffer, uint32_t size);
ArrayBuffer buffer() const;
uint32_t length() const;
void set(const Uint8Array &source);
Uint8Array subarray(uint32_t begin, uint32_t end);
void copyTo(char *destination) const;
QByteArray copyToQByteArray() const;
static void copy(char *destination, const Uint8Array &source);
static Uint8Array copyFrom(const char *buffer, uint32_t size);
static Uint8Array copyFrom(const QByteArray &buffer);
emscripten::val val() const;
private:
static emscripten::val constructor_();
emscripten::val m_uint8Array = emscripten::val::undefined();
};
class Q_CORE_EXPORT FileSystemWritableFileStream {
public:
FileSystemWritableFileStream() = default;
explicit FileSystemWritableFileStream(const emscripten::val &writableStream);
emscripten::val val() const;
private:
emscripten::val m_writableStream = emscripten::val::undefined();
};
class Q_CORE_EXPORT FileSystemFileHandle {
public:
FileSystemFileHandle() = default;
explicit FileSystemFileHandle(const emscripten::val &fileHandle);
std::string name() const;
std::string kind() const;
emscripten::val val() const;
private:
emscripten::val m_fileHandle = emscripten::val::undefined();
};
// EventCallback here for source compatibility; prefer using QWasmEventHandler directly
class Q_CORE_EXPORT EventCallback : public QWasmEventHandler
{
public:
EventCallback() = default;
EventCallback(EventCallback const&) = delete;
EventCallback& operator=(EventCallback const&) = delete;
EventCallback(emscripten::val element, const std::string &name,
const std::function<void(emscripten::val)> &fn);
};
struct PromiseCallbacks
{
std::function<void(emscripten::val)> thenFunc = [](emscripten::val) {};
std::function<void(emscripten::val)> catchFunc = [](emscripten::val) {};
std::function<void()> finallyFunc = []() {};
};
// Note: it is ok for the Promise object to go out of scope,
// the resources will be cleaned up in the finally handler.
class Q_CORE_EXPORT Promise {
public:
template<typename... Args>
Promise(emscripten::val target, QString methodName, Args... args) {
m_state = std::make_shared<State>();
m_state->m_promise = target.call<emscripten::val>(
methodName.toStdString().c_str(), std::forward<Args>(args)...);
if (m_state->m_promise.isUndefined() || m_state->m_promise["constructor"]["name"].as<std::string>() != "Promise") {
qFatal("This function did not return a promise");
}
addFinallyFunction([](){});
}
template<typename... Args>
Promise(emscripten::val target, const char *methodName, Args... args) {
m_state = std::make_shared<State>();
m_state->m_promise = target.call<emscripten::val>(
methodName, std::forward<Args>(args)...);
if (m_state->m_promise.isUndefined() || m_state->m_promise["constructor"]["name"].as<std::string>() != "Promise") {
qFatal("This function did not return a promise");
}
addFinallyFunction([](){});
}
Promise(emscripten::val promise) {
m_state = std::make_shared<State>();
m_state->m_promise = promise;
if (m_state->m_promise.isUndefined() || m_state->m_promise["constructor"]["name"].as<std::string>() != "Promise") {
qFatal("This function did not return a promise");
}
addFinallyFunction([](){});
}
Promise(const std::vector<Promise> &promises) {
std::vector<emscripten::val> all;
all.reserve(promises.size());
for (const auto &p : promises)
all.push_back(p.getPromise());
auto arr = emscripten::val::array(all);
m_state = std::make_shared<State>();
m_state->m_promise = emscripten::val::global("Promise").call<emscripten::val>("all", arr);
addFinallyFunction([](){});
}
Promise& addThenFunction(std::function<void(emscripten::val)> thenFunc);
Promise& addCatchFunction(std::function<void(emscripten::val)> catchFunc);
Promise& addFinallyFunction(std::function<void()> finallyFunc);
void suspendExclusive();
struct AwaitResult {
emscripten::val value = emscripten::val::undefined();
emscripten::val error = emscripten::val::undefined();
};
AwaitResult awaitExclusive();
emscripten::val getPromise() const;
public:
class State {
private:
friend class Promise;
State(const State&) = delete;
State(State&&) = delete;
State& operator=(const State&) = delete;
State& operator=(State&&) = delete;
public:
State() { ++s_numInstances; }
~State() { --s_numInstances; }
static size_t numInstances() { return s_numInstances; }
private:
emscripten::val m_promise = emscripten::val::undefined();
QList<uint32_t> m_handlers;
static size_t s_numInstances;
};
private:
std::shared_ptr<State> m_state;
public:
// Deprecated: To be backwards compatible
static uint32_t Q_CORE_EXPORT adoptPromise(emscripten::val promise, PromiseCallbacks callbacks, QList<uint32_t> *handlers = nullptr);
template<typename... Args>
static uint32_t make(emscripten::val target,
QString methodName,
PromiseCallbacks callbacks,
Args... args)
{
emscripten::val promiseObject = target.call<emscripten::val>(
methodName.toStdString().c_str(), std::forward<Args>(args)...);
if (promiseObject.isUndefined() || promiseObject["constructor"]["name"].as<std::string>() != "Promise") {
qFatal("This function did not return a promise");
}
return adoptPromise(std::move(promiseObject), std::move(callbacks));
}
template<typename... Args>
static void make(
QList<uint32_t> &handlers,
emscripten::val target,
QString methodName,
PromiseCallbacks callbacks,
Args... args)
{
emscripten::val promiseObject = target.call<emscripten::val>(
methodName.toStdString().c_str(), std::forward<Args>(args)...);
if (promiseObject.isUndefined() || promiseObject["constructor"]["name"].as<std::string>() != "Promise") {
qFatal("This function did not return a promise");
}
adoptPromise(std::move(promiseObject), std::move(callbacks), &handlers);
}
static void Q_CORE_EXPORT suspendExclusive(QList<uint32_t> handlerIndices);
static void Q_CORE_EXPORT all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks);
};
template<class F>
decltype(auto) bindForever(F wrappedCallback)
{
return wrappedCallback;
}
class Q_CORE_EXPORT BlobIODevice: public QIODevice
{
public:
BlobIODevice(Blob blob);
bool open(QIODeviceBase::OpenMode mode) override;
bool isSequential() const override;
qint64 size() const override;
bool seek(qint64 pos) override;
protected:
qint64 readData(char *data, qint64 maxSize) override;
qint64 writeData(const char *, qint64) override;
private:
Blob m_blob;
};
class Uint8ArrayIODevice: public QIODevice
{
public:
Uint8ArrayIODevice(Uint8Array array);
bool open(QIODevice::OpenMode mode) override;
bool isSequential() const override;
qint64 size() const override;
bool seek(qint64 pos) override;
protected:
qint64 readData(char *data, qint64 maxSize) override;
qint64 writeData(const char *data, qint64 size) override;
private:
Uint8Array m_array;
};
class Q_CORE_EXPORT FileSystemWritableFileStreamIODevice: public QIODevice
{
public:
FileSystemWritableFileStreamIODevice(FileSystemWritableFileStream stream);
bool open(QIODevice::OpenMode mode) override;
void close() override;
bool isSequential() const override;
qint64 size() const override;
bool seek(qint64 pos) override;
protected:
qint64 readData(char *data, qint64 maxSize) override;
qint64 writeData(const char *data, qint64 size) override;
private:
FileSystemWritableFileStream m_stream;
qint64 m_size = 0;
};
class Q_CORE_EXPORT FileSystemFileIODevice: public QIODevice
{
public:
FileSystemFileIODevice(FileSystemFileHandle fileHandle);
bool open(QIODevice::OpenMode mode) override;
void close() override;
bool isSequential() const override;
qint64 size() const override;
bool seek(qint64 pos) override;
protected:
qint64 readData(char *data, qint64 maxSize) override;
qint64 writeData(const char *data, qint64 size) override;
private:
FileSystemFileHandle m_fileHandle;
std::unique_ptr<BlobIODevice> m_blobDevice;
std::unique_ptr<FileSystemWritableFileStreamIODevice> m_writableDevice;
qint64 m_size = 0;
};
inline emscripten::val window()
{
static emscripten::val savedWindow = emscripten::val::global("window");
return savedWindow;
}
bool Q_CORE_EXPORT haveAsyncify();
bool Q_CORE_EXPORT haveJspi();
bool canBlockCallingThread();
}
QT_END_NAMESPACE
#endif