6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qfile.h>
8#include <QtCore/qmimedata.h>
10#include <emscripten/bind.h>
11#include <emscripten/emscripten.h>
12#include <emscripten/html5.h>
16#include <unordered_map>
31 volatile bool doIt =
false;
33 emscripten_set_wheel_callback(
"", 0, 0, NULL);
40struct ChunkedFileReader
44 std::function<
void()> onCompleted)
46 (
new ChunkedFileReader(
end, std::move(onCompleted), std::move(
file)))
51 ChunkedFileReader(uint32_t
end, std::function<
void()> onCompleted,
File file)
52 :
end(
end), onCompleted(std::move(onCompleted)),
file(std::move(
file))
56 void readNextChunk(uint32_t chunkBegin,
char *chunkBuffer)
63 const uint32_t nextChunkBegin = std::min(chunkBegin +
result.byteLength(),
end);
64 if (nextChunkBegin ==
end) {
69 char *nextChunkBuffer = chunkBuffer +
result.byteLength();
70 fileReader.
onLoad([
this, nextChunkBegin, nextChunkBuffer](emscripten::val) {
71 readNextChunk(nextChunkBegin, nextChunkBuffer);
73 const uint32_t nextChunkEnd = std::min(nextChunkBegin + chunkSize,
end);
78 static constexpr uint32_t chunkSize = 256 * 1024;
82 std::function<
void()> onCompleted;
86enum class CallbackType {
92void validateCallbacks(
const PromiseCallbacks&
callbacks) {
98#define THUNK_NAME(type, i) callbackThunk##type##i
113 class ThunkAllocation {
115 ThunkAllocation(
int thunkId, ThunkPool*
pool) : m_thunkId(thunkId), m_pool(
pool) {}
117 m_pool->free(m_thunkId);
121 int id()
const {
return m_thunkId; }
124 void bindToPromise(emscripten::val
target,
const PromiseCallbacks&
callbacks) {
130 emscripten::val::module_property(thunkName(CallbackType::Then,
id()).
data()));
135 emscripten::val::module_property(thunkName(CallbackType::Catch,
id()).
data()));
140 emscripten::val::module_property(thunkName(CallbackType::Finally,
id()).
data()));
150 std::iota(m_free.begin(), m_free.end(), 0);
153 void setThunkCallback(std::function<
void(
int, CallbackType, emscripten::val)> callback) {
154 m_callback = std::move(callback);
157 void allocateThunk(std::function<
void(std::unique_ptr<ThunkAllocation>)> onAllocated) {
158 if (m_free.empty()) {
159 m_pendingAllocations.push_back(std::move(onAllocated));
163 const int thunkId = m_free.back();
165 onAllocated(std::make_unique<ThunkAllocation>(thunkId,
this));
171 case CallbackType::Then:
173 case CallbackType::Catch:
175 case CallbackType::Finally:
178 }()).arg(
i).toLatin1();
181 static ThunkPool*
get();
184 static void THUNK_NAME(Then, i)(emscripten::val result) \
186 get()->onThunkCalled(i, CallbackType::Then, std::move(result)); \
188 static void THUNK_NAME(Catch, i)(emscripten::val result) \
190 get()->onThunkCalled(i, CallbackType::Catch, std::move(result)); \
192 static void THUNK_NAME(Finally, i)() \
194 get()->onThunkCalled(i, CallbackType::Finally, emscripten::val::undefined()); \
205 void onThunkCalled(
int index, CallbackType
type, emscripten::val
result) {
209 void free(
int thunkId) {
210 if (m_pendingAllocations.empty()) {
212 m_free.push_back(thunkId);
217 auto allocation = m_pendingAllocations.back();
218 m_pendingAllocations.pop_back();
219 allocation(std::make_unique<ThunkAllocation>(thunkId,
this));
222 std::function<
void(
int, CallbackType, emscripten::val)> m_callback;
224 std::vector<int> m_free = std::vector<int>(poolSize);
225 std::vector<std::function<
void(std::unique_ptr<ThunkAllocation>)>> m_pendingAllocations;
230ThunkPool* ThunkPool::get()
235#define CALLBACK_BINDING(i) \
236 emscripten::function(ThunkPool::thunkName(CallbackType::Then, i).data(), \
237 &ThunkPool::THUNK_NAME(Then, i)); \
238 emscripten::function(ThunkPool::thunkName(CallbackType::Catch, i).data(), \
239 &ThunkPool::THUNK_NAME(Catch, i)); \
240 emscripten::function(ThunkPool::thunkName(CallbackType::Finally, i).data(), \
241 &ThunkPool::THUNK_NAME(Finally, i));
250#undef CALLBACK_BINDING
253class WebPromiseManager
257 ~WebPromiseManager();
259 WebPromiseManager(
const WebPromiseManager&
other) =
delete;
260 WebPromiseManager(WebPromiseManager&&
other) =
delete;
261 WebPromiseManager& operator=(
const WebPromiseManager&
other) =
delete;
262 WebPromiseManager& operator=(WebPromiseManager&&
other) =
delete;
266 static WebPromiseManager*
get();
269 struct RegistryEntry {
274 static std::optional<CallbackType> parseCallbackType(emscripten::val callbackType);
276 void subscribeToJsPromiseCallbacks(
int i,
const PromiseCallbacks&
callbacks, emscripten::val jsContextfulPromise);
277 void promiseThunkCallback(
int i, CallbackType
type, emscripten::val
result);
279 void registerPromise(std::unique_ptr<ThunkPool::ThunkAllocation>
allocation, PromiseCallbacks promise);
280 void unregisterPromise(ThunkId
context);
282 std::array<RegistryEntry, ThunkPool::poolSize> m_promiseRegistry;
287WebPromiseManager::WebPromiseManager()
289 ThunkPool::get()->setThunkCallback(std::bind(
290 &WebPromiseManager::promiseThunkCallback,
this,
291 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
294std::optional<CallbackType>
295WebPromiseManager::parseCallbackType(emscripten::val callbackType)
297 if (!callbackType.isString())
300 const std::string
data = callbackType.as<std::string>();
302 return CallbackType::Then;
304 return CallbackType::Catch;
305 if (
data ==
"finally")
306 return CallbackType::Finally;
310WebPromiseManager::~WebPromiseManager() =
default;
312WebPromiseManager *WebPromiseManager::get()
314 return webPromiseManager();
317void WebPromiseManager::promiseThunkCallback(
int context, CallbackType
type, emscripten::val
result)
319 auto* promiseState = &m_promiseRegistry[
context];
321 auto*
callbacks = &promiseState->callbacks;
322 bool expectingOtherCallbacks;
324 case CallbackType::Then:
329 case CallbackType::Catch:
333 case CallbackType::Finally:
335 expectingOtherCallbacks =
false;
339 if (!expectingOtherCallbacks)
343void WebPromiseManager::registerPromise(
344 std::unique_ptr<ThunkPool::ThunkAllocation>
allocation,
348 m_promiseRegistry[
id] =
352void WebPromiseManager::unregisterPromise(ThunkId
context)
354 m_promiseRegistry[
context] = {};
357void WebPromiseManager::adoptPromise(emscripten::val
target, PromiseCallbacks
callbacks) {
358 ThunkPool::get()->allocateThunk([=](std::unique_ptr<ThunkPool::ThunkAllocation>
allocation) {
363#if defined(QT_STATIC)
365EM_JS(
bool, jsHaveAsyncify, (), {
return typeof Asyncify !==
"undefined"; });
366EM_JS(
bool, jsHaveJspi, (),
367 {
return typeof Asyncify !==
"undefined" && !!Asyncify.makeAsyncFunction && !!WebAssembly.Function; });
371bool jsHaveAsyncify() {
return false; }
372bool jsHaveJspi() {
return false; }
376struct DataTransferReader
379 using DoneCallback = std::function<
void(std::unique_ptr<QMimeData>)>;
381 static std::shared_ptr<CancellationFlag>
read(emscripten::val webDataTransfer,
383 DoneCallback onCompleted)
385 auto cancellationFlag = std::make_shared<CancellationFlag>();
386 (
new DataTransferReader(std::move(onCompleted), std::move(imageReader), cancellationFlag))
387 ->
read(webDataTransfer);
388 return cancellationFlag;
391 ~DataTransferReader() =
default;
394 DataTransferReader(DoneCallback onCompleted, std::function<
QVariant(
QByteArray)> imageReader,
395 std::shared_ptr<CancellationFlag> cancellationFlag)
397 imageReader(
std::move(imageReader)),
398 onCompleted(
std::move(onCompleted)),
399 cancellationFlag(cancellationFlag)
403 void read(emscripten::val webDataTransfer)
405 enum class ItemKind {
410 const auto items = webDataTransfer[
"items"];
411 for (
int i = 0;
i <
items[
"length"].as<
int>(); ++
i) {
413 const auto itemKind =
414 item[
"kind"].as<std::string>() ==
"string" ? ItemKind::String : ItemKind::
File;
418 case ItemKind::File: {
424 file.stream(fileContent.data(), [
this, itemMimeType, fileContent]() {
425 if (!fileContent.isEmpty()) {
426 if (itemMimeType.startsWith(
"image/")) {
427 mimeData->setImageData(imageReader(fileContent));
429 mimeData->setData(itemMimeType, fileContent.data());
437 case ItemKind::String:
443 const QString data = QString::fromJsString(webDataTransfer.call<emscripten::val>(
444 "getData", emscripten::val(itemMimeType.toStdString())));
446 if (!
data.isEmpty()) {
447 if (itemMimeType ==
"text/html")
449 else if (itemMimeType.isEmpty() || itemMimeType ==
"text/plain")
464 if (doneCount < fileCount)
467 std::unique_ptr<DataTransferReader> deleteThisLater(
this);
468 if (!cancellationFlag.expired())
474 std::unique_ptr<QMimeData>
mimeData;
476 DoneCallback onCompleted;
478 std::weak_ptr<CancellationFlag> cancellationFlag;
483ArrayBuffer::ArrayBuffer(uint32_t
size)
485 m_arrayBuffer = emscripten::val::global(
"ArrayBuffer").new_(
size);
488ArrayBuffer::ArrayBuffer(
const emscripten::val &arrayBuffer)
489 :m_arrayBuffer(arrayBuffer)
496 if (m_arrayBuffer.isUndefined() || m_arrayBuffer.isNull())
499 return m_arrayBuffer[
"byteLength"].as<uint32_t>();
504 return m_arrayBuffer;
515 return m_blob[
"size"].as<uint32_t>();
522 emscripten::val contentArray = emscripten::val::array();
523 contentArray.call<
void>(
"push", contentCopy.
val());
524 emscripten::val
type = emscripten::val::object();
526 return Blob(emscripten::val::global(
"Blob").new_(contentArray,
type));
553 return m_file[
"name"].as<std::string>();
558 return uint64_t(m_file[
"size"].as<uint53_t>());
563 return m_blob[
"type"].as<std::string>();
569 std::function<
void()> completed)
const
583 return m_file[
"type"].as<std::string>();
592 :m_fileList(fileList)
599 return m_fileList[
"length"].as<
int>();
624 m_fileReader.call<
void>(
"readAsArrayBuffer", blob.m_blob);
630 m_onLoad = std::make_unique<EventCallback>(m_fileReader,
"load",
onLoad);
636 m_onError = std::make_unique<EventCallback>(m_fileReader,
"error",
onError);
642 m_onAbort = std::make_unique<EventCallback>(m_fileReader,
"abort",
onAbort);
657: m_uint8Array(uint8Array)
697 return m_uint8Array[
"length"].as<uint32_t>();
702 m_uint8Array.call<
void>(
"set",
source.m_uint8Array);
714 if (
length() > std::numeric_limits<qsizetype>::max())
720 return destinationArray;
748emscripten::val Uint8Array::heap_()
750 return emscripten::val::module_property(
"HEAPU8");
753emscripten::val Uint8Array::constructor_()
755 return emscripten::val::global(
"Uint8Array");
763 if (m_element[contextPropertyName(m_eventName).c_str()].as<intptr_t>() ==
intptr_t(
this)) {
764 m_element.set(contextPropertyName(m_eventName).c_str(), emscripten::val::undefined());
765 m_element.set((std::string(
"on") + m_eventName).c_str(), emscripten::val::undefined());
775 "Only one event callback of type currently supported with EventCallback");
776 m_element.set(contextPropertyName(m_eventName).c_str(), emscripten::val(
intptr_t(
this)));
777 m_element.set((std::string(
"on") + m_eventName).c_str(), emscripten::val::module_property(
"qtStdWebEventCallbackActivate"));
782 emscripten::val
target =
event[
"currentTarget"];
783 std::string eventName =
event[
"type"].as<std::string>();
784 emscripten::val
property =
target[contextPropertyName(eventName)];
792std::string EventCallback::contextPropertyName(
const std::string &eventName)
794 return std::string(
"data-qtEventCallbackContext") + eventName;
805 WebPromiseManager::get()->adoptPromise(
806 std::move(promiseObject), std::move(
callbacks));
811 std::map<int, emscripten::val>
results;
812 int remainingThenCallbacks;
813 int remainingFinallyCallbacks;
818 auto state = std::make_shared<State>();
819 state->remainingThenCallbacks =
state->remainingFinallyCallbacks = promises.size();
821 for (
size_t i = 0;
i < promises.size(); ++
i) {
825 state->results.emplace(
i, std::move(partialResult));
826 if (!--(
state->remainingThenCallbacks)) {
827 std::vector<emscripten::val> transformed;
829 transformed.push_back(std::move(
data.second));
831 callbacks.thenFunc(emscripten::val::array(std::move(transformed)));
841 if (!--(
state->remainingFinallyCallbacks)) {
850 adoptPromise(std::move(promises.at(
i)), std::move(individualPromiseCallback));
857 static bool HaveAsyncify = jsHaveAsyncify();
863 static bool HaveJspi = jsHaveJspi();
867std::shared_ptr<CancellationFlag>
869 std::function<
void(std::unique_ptr<QMimeData>)> onDone)
871 return DataTransferReader::read(webDataTransfer, std::move(imageReader), std::move(onDone));
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
qint64 size() const override
\reimp
void setHtml(const QString &html)
Sets html as the HTML (MIME type text/html) used to represent the data.
void setData(const QString &mimetype, const QByteArray &data)
Sets the data associated with the MIME type given by mimeType to the specified data.
void setText(const QString &text)
Sets text as the plain text (MIME type text/plain) used to represent the data.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromStdString(const std::string &s)
uint32_t byteLength() const
Blob(const emscripten::val &blob)
static Blob copyFrom(const char *buffer, uint32_t size, std::string mimeType)
static void activate(emscripten::val event)
File item(int index) const
emscripten::val val() const
File operator[](int index) const
void onAbort(const std::function< void(emscripten::val)> &onAbort)
ArrayBuffer result() const
void onLoad(const std::function< void(emscripten::val)> &onLoad)
void readAsArrayBuffer(const Blob &blob) const
void onError(const std::function< void(emscripten::val)> &onError)
Blob slice(uint64_t begin, uint64_t end) const
void stream(uint32_t offset, uint32_t length, char *buffer, std::function< void()> completed) const
void copyTo(char *destination) const
void set(const Uint8Array &source)
ArrayBuffer buffer() const
QByteArray copyToQByteArray() const
Uint8Array(const emscripten::val &uint8Array)
static Uint8Array copyFrom(const char *buffer, uint32_t size)
static void copy(char *destination, const Uint8Array &source)
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
void adoptPromise(emscripten::val promiseObject, PromiseCallbacks callbacks)
void all(std::vector< emscripten::val > promises, PromiseCallbacks callbacks)
std::shared_ptr< CancellationFlag > readDataTransfer(emscripten::val webDataTransfer, std::function< QVariant(QByteArray)> imageReader, std::function< void(std::unique_ptr< QMimeData >)> onDone)
static void usePotentialyUnusedSymbols()
EMSCRIPTEN_BINDINGS(qtStdwebCalback)
Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
DBusConnection const char DBusError * error
static QDBusError::ErrorType get(const char *name)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLsizei GLsizei GLchar * source
#define Q_ASSERT_X(cond, x, msg)
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
std::unique_ptr< ThunkPool::ThunkAllocation > allocation
PromiseCallbacks callbacks
static constexpr size_t poolSize
#define CALLBACK_BINDING(i)
#define QStringLiteral(str)
ReturnedValue read(const char *data)
std::function< void(emscripten::val)> thenFunc
std::function< void()> finallyFunc
std::function< void(emscripten::val)> catchFunc