Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qstdweb.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qstdweb_p.h"
5
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qfile.h>
8#include <QtCore/qmimedata.h>
9
10#include <emscripten/bind.h>
11#include <emscripten/emscripten.h>
12#include <emscripten/html5.h>
13#include <cstdint>
14#include <iostream>
15
16#include <unordered_map>
17
19
20namespace qstdweb {
21
23{
24 // Using this adds a reference on JSEvents and specialHTMLTargets which are always exported.
25 // This hack is needed as it is currently impossible to specify a dollar sign in
26 // target_link_options. The following is impossible:
27 // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$JSEvents
28 // TODO(mikolajboc): QTBUG-108444, review this when cmake gets fixed.
29 // Volatile is to make this unoptimizable, so that the function is referenced, but is not
30 // called at runtime.
31 volatile bool doIt = false;
32 if (doIt)
33 emscripten_set_wheel_callback("", 0, 0, NULL);
34}
35
37typedef double uint53_t; // see Number.MAX_SAFE_INTEGER
38namespace {
39// Reads file in chunks in order to avoid holding two copies in memory at the same time
40struct ChunkedFileReader
41{
42public:
43 static void read(File file, char *buffer, uint32_t offset, uint32_t end,
44 std::function<void()> onCompleted)
45 {
46 (new ChunkedFileReader(end, std::move(onCompleted), std::move(file)))
47 ->readNextChunk(offset, buffer);
48 }
49
50private:
51 ChunkedFileReader(uint32_t end, std::function<void()> onCompleted, File file)
52 : end(end), onCompleted(std::move(onCompleted)), file(std::move(file))
53 {
54 }
55
56 void readNextChunk(uint32_t chunkBegin, char *chunkBuffer)
57 {
58 // Copy current chunk from JS memory to Wasm memory
59 qstdweb::ArrayBuffer result = fileReader.result();
60 qstdweb::Uint8Array(result).copyTo(chunkBuffer);
61
62 // Read next chunk if not at buffer end
63 const uint32_t nextChunkBegin = std::min(chunkBegin + result.byteLength(), end);
64 if (nextChunkBegin == end) {
65 onCompleted();
66 delete this;
67 return;
68 }
69 char *nextChunkBuffer = chunkBuffer + result.byteLength();
70 fileReader.onLoad([this, nextChunkBegin, nextChunkBuffer](emscripten::val) {
71 readNextChunk(nextChunkBegin, nextChunkBuffer);
72 });
73 const uint32_t nextChunkEnd = std::min(nextChunkBegin + chunkSize, end);
74 qstdweb::Blob blob = file.slice(nextChunkBegin, nextChunkEnd);
75 fileReader.readAsArrayBuffer(blob);
76 }
77
78 static constexpr uint32_t chunkSize = 256 * 1024;
79
80 qstdweb::FileReader fileReader;
81 uint32_t end;
82 std::function<void()> onCompleted;
83 File file;
84};
85
86enum class CallbackType {
87 Then,
88 Catch,
89 Finally,
90};
91
92void validateCallbacks(const PromiseCallbacks& callbacks) {
94}
95
96using ThunkId = int;
97
98#define THUNK_NAME(type, i) callbackThunk##type##i
99
100// A resource pool for exported promise thunk functions. ThunkPool::poolSize sets of
101// 3 promise thunks (then, catch, finally) are exported and can be used by promises
102// in C++. To allocate a thunk, call allocateThunk. When a thunk is ready for use,
103// a callback with allocation RAII object ThunkAllocation will be returned. Deleting
104// the object frees the thunk and automatically makes any pending allocateThunk call
105// run its callback with a free thunk slot.
106class ThunkPool {
107public:
108 static constexpr size_t poolSize = 4;
109
110 // An allocation for a thunk function set. Following the RAII pattern, destruction of
111 // this objects frees a corresponding thunk pool entry.
112 // To actually make the thunks react to a js promise's callbacks, call bindToPromise.
113 class ThunkAllocation {
114 public:
115 ThunkAllocation(int thunkId, ThunkPool* pool) : m_thunkId(thunkId), m_pool(pool) {}
116 ~ThunkAllocation() {
117 m_pool->free(m_thunkId);
118 }
119
120 // The id of the underlaying thunk set
121 int id() const { return m_thunkId; }
122
123 // Binds the corresponding thunk set to the js promise 'target'.
124 void bindToPromise(emscripten::val target, const PromiseCallbacks& callbacks) {
125 using namespace emscripten;
126
127 if (Q_LIKELY(callbacks.thenFunc)) {
128 target = target.call<val>(
129 "then",
130 emscripten::val::module_property(thunkName(CallbackType::Then, id()).data()));
131 }
132 if (callbacks.catchFunc) {
133 target = target.call<val>(
134 "catch",
135 emscripten::val::module_property(thunkName(CallbackType::Catch, id()).data()));
136 }
137 if (callbacks.finallyFunc) {
138 target = target.call<val>(
139 "finally",
140 emscripten::val::module_property(thunkName(CallbackType::Finally, id()).data()));
141 }
142 }
143
144 private:
145 int m_thunkId;
146 ThunkPool* m_pool;
147 };
148
149 ThunkPool() {
150 std::iota(m_free.begin(), m_free.end(), 0);
151 }
152
153 void setThunkCallback(std::function<void(int, CallbackType, emscripten::val)> callback) {
154 m_callback = std::move(callback);
155 }
156
157 void allocateThunk(std::function<void(std::unique_ptr<ThunkAllocation>)> onAllocated) {
158 if (m_free.empty()) {
159 m_pendingAllocations.push_back(std::move(onAllocated));
160 return;
161 }
162
163 const int thunkId = m_free.back();
164 m_free.pop_back();
165 onAllocated(std::make_unique<ThunkAllocation>(thunkId, this));
166 }
167
168 static QByteArray thunkName(CallbackType type, size_t i) {
169 return QStringLiteral("promiseCallback%1%2").arg([type]() -> QString {
170 switch (type) {
171 case CallbackType::Then:
172 return QStringLiteral("Then");
173 case CallbackType::Catch:
174 return QStringLiteral("Catch");
175 case CallbackType::Finally:
176 return QStringLiteral("Finally");
177 }
178 }()).arg(i).toLatin1();
179 }
180
181 static ThunkPool* get();
182
183#define THUNK(i) \
184 static void THUNK_NAME(Then, i)(emscripten::val result) \
185 { \
186 get()->onThunkCalled(i, CallbackType::Then, std::move(result)); \
187 } \
188 static void THUNK_NAME(Catch, i)(emscripten::val result) \
189 { \
190 get()->onThunkCalled(i, CallbackType::Catch, std::move(result)); \
191 } \
192 static void THUNK_NAME(Finally, i)() \
193 { \
194 get()->onThunkCalled(i, CallbackType::Finally, emscripten::val::undefined()); \
195 }
196
197 THUNK(0);
198 THUNK(1);
199 THUNK(2);
200 THUNK(3);
201
202#undef THUNK
203
204private:
205 void onThunkCalled(int index, CallbackType type, emscripten::val result) {
206 m_callback(index, type, std::move(result));
207 }
208
209 void free(int thunkId) {
210 if (m_pendingAllocations.empty()) {
211 // Return the thunk to the free pool
212 m_free.push_back(thunkId);
213 return;
214 }
215
216 // Take the next enqueued allocation and reuse the thunk
217 auto allocation = m_pendingAllocations.back();
218 m_pendingAllocations.pop_back();
219 allocation(std::make_unique<ThunkAllocation>(thunkId, this));
220 }
221
222 std::function<void(int, CallbackType, emscripten::val)> m_callback;
223
224 std::vector<int> m_free = std::vector<int>(poolSize);
225 std::vector<std::function<void(std::unique_ptr<ThunkAllocation>)>> m_pendingAllocations;
226};
227
228Q_GLOBAL_STATIC(ThunkPool, g_thunkPool)
229
230ThunkPool* ThunkPool::get()
231{
232 return g_thunkPool;
233}
234
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));
242
243EMSCRIPTEN_BINDINGS(qtThunkPool) {
248}
249
250#undef CALLBACK_BINDING
251#undef THUNK_NAME
252
253class WebPromiseManager
254{
255public:
256 WebPromiseManager();
257 ~WebPromiseManager();
258
259 WebPromiseManager(const WebPromiseManager& other) = delete;
260 WebPromiseManager(WebPromiseManager&& other) = delete;
261 WebPromiseManager& operator=(const WebPromiseManager& other) = delete;
262 WebPromiseManager& operator=(WebPromiseManager&& other) = delete;
263
264 void adoptPromise(emscripten::val target, PromiseCallbacks callbacks);
265
266 static WebPromiseManager* get();
267
268private:
269 struct RegistryEntry {
270 PromiseCallbacks callbacks;
271 std::unique_ptr<ThunkPool::ThunkAllocation> allocation;
272 };
273
274 static std::optional<CallbackType> parseCallbackType(emscripten::val callbackType);
275
276 void subscribeToJsPromiseCallbacks(int i, const PromiseCallbacks& callbacks, emscripten::val jsContextfulPromise);
277 void promiseThunkCallback(int i, CallbackType type, emscripten::val result);
278
279 void registerPromise(std::unique_ptr<ThunkPool::ThunkAllocation> allocation, PromiseCallbacks promise);
280 void unregisterPromise(ThunkId context);
281
282 std::array<RegistryEntry, ThunkPool::poolSize> m_promiseRegistry;
283};
284
285Q_GLOBAL_STATIC(WebPromiseManager, webPromiseManager)
286
287WebPromiseManager::WebPromiseManager()
288{
289 ThunkPool::get()->setThunkCallback(std::bind(
290 &WebPromiseManager::promiseThunkCallback, this,
291 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
292}
293
294std::optional<CallbackType>
295WebPromiseManager::parseCallbackType(emscripten::val callbackType)
296{
297 if (!callbackType.isString())
298 return std::nullopt;
299
300 const std::string data = callbackType.as<std::string>();
301 if (data == "then")
302 return CallbackType::Then;
303 if (data == "catch")
304 return CallbackType::Catch;
305 if (data == "finally")
306 return CallbackType::Finally;
307 return std::nullopt;
308}
309
310WebPromiseManager::~WebPromiseManager() = default;
311
312WebPromiseManager *WebPromiseManager::get()
313{
314 return webPromiseManager();
315}
316
317void WebPromiseManager::promiseThunkCallback(int context, CallbackType type, emscripten::val result)
318{
319 auto* promiseState = &m_promiseRegistry[context];
320
321 auto* callbacks = &promiseState->callbacks;
322 bool expectingOtherCallbacks;
323 switch (type) {
324 case CallbackType::Then:
326 // At this point, if there is no finally function, we are sure that the Catch callback won't be issued.
327 expectingOtherCallbacks = !!callbacks->finallyFunc;
328 break;
329 case CallbackType::Catch:
331 expectingOtherCallbacks = !!callbacks->finallyFunc;
332 break;
333 case CallbackType::Finally:
335 expectingOtherCallbacks = false;
336 break;
337 }
338
339 if (!expectingOtherCallbacks)
340 unregisterPromise(context);
341}
342
343void WebPromiseManager::registerPromise(
344 std::unique_ptr<ThunkPool::ThunkAllocation> allocation,
345 PromiseCallbacks callbacks)
346{
347 const ThunkId id = allocation->id();
348 m_promiseRegistry[id] =
349 RegistryEntry {std::move(callbacks), std::move(allocation)};
350}
351
352void WebPromiseManager::unregisterPromise(ThunkId context)
353{
354 m_promiseRegistry[context] = {};
355}
356
357void WebPromiseManager::adoptPromise(emscripten::val target, PromiseCallbacks callbacks) {
358 ThunkPool::get()->allocateThunk([=](std::unique_ptr<ThunkPool::ThunkAllocation> allocation) {
359 allocation->bindToPromise(std::move(target), callbacks);
360 registerPromise(std::move(allocation), std::move(callbacks));
361 });
362}
363#if defined(QT_STATIC)
364
365EM_JS(bool, jsHaveAsyncify, (), { return typeof Asyncify !== "undefined"; });
366EM_JS(bool, jsHaveJspi, (),
367 { return typeof Asyncify !== "undefined" && !!Asyncify.makeAsyncFunction && !!WebAssembly.Function; });
368
369#else
370
371bool jsHaveAsyncify() { return false; }
372bool jsHaveJspi() { return false; }
373
374#endif
375
376struct DataTransferReader
377{
378public:
379 using DoneCallback = std::function<void(std::unique_ptr<QMimeData>)>;
380
381 static std::shared_ptr<CancellationFlag> read(emscripten::val webDataTransfer,
382 std::function<QVariant(QByteArray)> imageReader,
383 DoneCallback onCompleted)
384 {
385 auto cancellationFlag = std::make_shared<CancellationFlag>();
386 (new DataTransferReader(std::move(onCompleted), std::move(imageReader), cancellationFlag))
387 ->read(webDataTransfer);
388 return cancellationFlag;
389 }
390
391 ~DataTransferReader() = default;
392
393private:
394 DataTransferReader(DoneCallback onCompleted, std::function<QVariant(QByteArray)> imageReader,
395 std::shared_ptr<CancellationFlag> cancellationFlag)
396 : mimeData(std::make_unique<QMimeData>()),
397 imageReader(std::move(imageReader)),
398 onCompleted(std::move(onCompleted)),
399 cancellationFlag(cancellationFlag)
400 {
401 }
402
403 void read(emscripten::val webDataTransfer)
404 {
405 enum class ItemKind {
406 File,
407 String,
408 };
409
410 const auto items = webDataTransfer["items"];
411 for (int i = 0; i < items["length"].as<int>(); ++i) {
412 const auto item = items[i];
413 const auto itemKind =
414 item["kind"].as<std::string>() == "string" ? ItemKind::String : ItemKind::File;
415 const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
416
417 switch (itemKind) {
418 case ItemKind::File: {
419 ++fileCount;
420
421 qstdweb::File file(item.call<emscripten::val>("getAsFile"));
422
423 QByteArray fileContent(file.size(), Qt::Uninitialized);
424 file.stream(fileContent.data(), [this, itemMimeType, fileContent]() {
425 if (!fileContent.isEmpty()) {
426 if (itemMimeType.startsWith("image/")) {
427 mimeData->setImageData(imageReader(fileContent));
428 } else {
429 mimeData->setData(itemMimeType, fileContent.data());
430 }
431 }
432 ++doneCount;
433 onFileRead();
434 });
435 break;
436 }
437 case ItemKind::String:
438 if (itemMimeType.contains("STRING", Qt::CaseSensitive)
439 || itemMimeType.contains("TEXT", Qt::CaseSensitive)) {
440 break;
441 }
442 QString a;
443 const QString data = QString::fromJsString(webDataTransfer.call<emscripten::val>(
444 "getData", emscripten::val(itemMimeType.toStdString())));
445
446 if (!data.isEmpty()) {
447 if (itemMimeType == "text/html")
449 else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
450 mimeData->setText(data); // the type can be empty
451 else
452 mimeData->setData(itemMimeType, data.toLocal8Bit());
453 }
454 break;
455 }
456 }
457
458 onFileRead();
459 }
460
461 void onFileRead()
462 {
463 Q_ASSERT(doneCount <= fileCount);
464 if (doneCount < fileCount)
465 return;
466
467 std::unique_ptr<DataTransferReader> deleteThisLater(this);
468 if (!cancellationFlag.expired())
469 onCompleted(std::move(mimeData));
470 }
471
472 int fileCount = 0;
473 int doneCount = 0;
474 std::unique_ptr<QMimeData> mimeData;
475 std::function<QVariant(QByteArray)> imageReader;
476 DoneCallback onCompleted;
477
478 std::weak_ptr<CancellationFlag> cancellationFlag;
479};
480
481} // namespace
482
483ArrayBuffer::ArrayBuffer(uint32_t size)
484{
485 m_arrayBuffer = emscripten::val::global("ArrayBuffer").new_(size);
486}
487
488ArrayBuffer::ArrayBuffer(const emscripten::val &arrayBuffer)
489 :m_arrayBuffer(arrayBuffer)
490{
491
492}
493
495{
496 if (m_arrayBuffer.isUndefined() || m_arrayBuffer.isNull())
497 return 0;
498
499 return m_arrayBuffer["byteLength"].as<uint32_t>();
500}
501
502emscripten::val ArrayBuffer::val()
503{
504 return m_arrayBuffer;
505}
506
507Blob::Blob(const emscripten::val &blob)
508 :m_blob(blob)
509{
510
511}
512
513uint32_t Blob::size() const
514{
515 return m_blob["size"].as<uint32_t>();
516}
517
518Blob Blob::copyFrom(const char *buffer, uint32_t size, std::string mimeType)
519{
521
522 emscripten::val contentArray = emscripten::val::array();
523 contentArray.call<void>("push", contentCopy.val());
524 emscripten::val type = emscripten::val::object();
525 type.set("type", std::move(mimeType));
526 return Blob(emscripten::val::global("Blob").new_(contentArray, type));
527}
528
529// Copies content from the given buffer into a Blob object
530Blob Blob::copyFrom(const char *buffer, uint32_t size)
531{
532 return copyFrom(buffer, size, "application/octet-stream");
533}
534
535emscripten::val Blob::val()
536{
537 return m_blob;
538}
539
540File::File(const emscripten::val &file)
541:m_file(file)
542{
543
544}
545
546Blob File::slice(uint64_t begin, uint64_t end) const
547{
548 return Blob(m_file.call<emscripten::val>("slice", uint53_t(begin), uint53_t(end)));
549}
550
551std::string File::name() const
552{
553 return m_file["name"].as<std::string>();
554}
555
556uint64_t File::size() const
557{
558 return uint64_t(m_file["size"].as<uint53_t>());
559}
560
561std::string Blob::type() const
562{
563 return m_blob["type"].as<std::string>();
564}
565
566// Streams partial file content into the given buffer asynchronously. The completed
567// callback is called on completion.
568void File::stream(uint32_t offset, uint32_t length, char *buffer,
569 std::function<void()> completed) const
570{
571 ChunkedFileReader::read(*this, buffer, offset, offset + length, std::move(completed));
572}
573
574// Streams file content into the given buffer asynchronously. The completed
575// callback is called on completion.
576void File::stream(char *buffer, std::function<void()> completed) const
577{
578 stream(0, size(), buffer, std::move(completed));
579}
580
581std::string File::type() const
582{
583 return m_file["type"].as<std::string>();
584}
585
586emscripten::val File::val()
587{
588 return m_file;
589}
590
591FileList::FileList(const emscripten::val &fileList)
592 :m_fileList(fileList)
593{
594
595}
596
598{
599 return m_fileList["length"].as<int>();
600}
601
603{
604 return File(m_fileList[index]);
605}
606
608{
609 return item(index);
610}
611
612emscripten::val FileList::val() const
613{
614 return m_fileList;
615}
616
618{
619 return ArrayBuffer(m_fileReader["result"]);
620}
621
622void FileReader::readAsArrayBuffer(const Blob &blob) const
623{
624 m_fileReader.call<void>("readAsArrayBuffer", blob.m_blob);
625}
626
627void FileReader::onLoad(const std::function<void(emscripten::val)> &onLoad)
628{
629 m_onLoad.reset();
630 m_onLoad = std::make_unique<EventCallback>(m_fileReader, "load", onLoad);
631}
632
633void FileReader::onError(const std::function<void(emscripten::val)> &onError)
634{
635 m_onError.reset();
636 m_onError = std::make_unique<EventCallback>(m_fileReader, "error", onError);
637}
638
639void FileReader::onAbort(const std::function<void(emscripten::val)> &onAbort)
640{
641 m_onAbort.reset();
642 m_onAbort = std::make_unique<EventCallback>(m_fileReader, "abort", onAbort);
643}
644
645emscripten::val FileReader::val()
646{
647 return m_fileReader;
648}
649
651{
652 return Uint8Array(heap_());
653}
654
655// Constructs a Uint8Array which references the given emscripten::val, which must contain a JS Unit8Array
656Uint8Array::Uint8Array(const emscripten::val &uint8Array)
657: m_uint8Array(uint8Array)
658{
659
660}
661
662// Constructs a Uint8Array which references an ArrayBuffer
664: m_uint8Array(Uint8Array::constructor_().new_(buffer.m_arrayBuffer))
665{
666
667}
668
669// Constructs a Uint8Array which references a view into an ArrayBuffer
671: m_uint8Array(Uint8Array::constructor_().new_(buffer.m_arrayBuffer, offset, length))
672{
673
674}
675
676// Constructs a Uint8Array which references an area on the heap.
677Uint8Array::Uint8Array(const char *buffer, uint32_t size)
678:m_uint8Array(Uint8Array::constructor_().new_(Uint8Array::heap().buffer().m_arrayBuffer, uintptr_t(buffer), size))
679{
680
681}
682
683// Constructs a Uint8Array which allocates and references a new ArrayBuffer with the given size.
685: m_uint8Array(Uint8Array::constructor_().new_(size))
686{
687
688}
689
691{
692 return ArrayBuffer(m_uint8Array["buffer"]);
693}
694
695uint32_t Uint8Array::length() const
696{
697 return m_uint8Array["length"].as<uint32_t>();
698}
699
701{
702 m_uint8Array.call<void>("set", source.m_uint8Array); // copies source content
703}
704
705// Copies the Uint8Array content to a destination on the heap
707{
708 Uint8Array(destination, length()).set(*this);
709}
710
711// Copies the Uint8Array content to a destination QByteArray
713{
714 if (length() > std::numeric_limits<qsizetype>::max())
715 return QByteArray();
716
717 QByteArray destinationArray;
718 destinationArray.resize(length());
719 copyTo(destinationArray.data());
720 return destinationArray;
721}
722
723// Copies the Uint8Array content to a destination on the heap
725{
727}
728
729// Copies content from a source on the heap to a new Uint8Array object
731{
732 Uint8Array contentCopy(size);
733 contentCopy.set(Uint8Array(buffer, size));
734 return contentCopy;
735}
736
737// Copies content from a QByteArray to a new Uint8Array object
739{
740 return copyFrom(buffer.constData(), buffer.size());
741}
742
743emscripten::val Uint8Array::val()
744{
745 return m_uint8Array;
746}
747
748emscripten::val Uint8Array::heap_()
749{
750 return emscripten::val::module_property("HEAPU8");
751}
752
753emscripten::val Uint8Array::constructor_()
754{
755 return emscripten::val::global("Uint8Array");
756}
757
758// Registers a callback function for a named event on the given element. The event
759// name must be the name as returned by the Event.type property: e.g. "load", "error".
761{
762 // Clean up if this instance's callback is still installed on the element
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());
766 }
767}
768
769EventCallback::EventCallback(emscripten::val element, const std::string &name, const std::function<void(emscripten::val)> &fn)
770 :m_element(element)
771 ,m_eventName(name)
772 ,m_fn(fn)
773{
774 Q_ASSERT_X(m_element[contextPropertyName(m_eventName)].isUndefined(), Q_FUNC_INFO,
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"));
778}
779
780void EventCallback::activate(emscripten::val event)
781{
782 emscripten::val target = event["currentTarget"];
783 std::string eventName = event["type"].as<std::string>();
784 emscripten::val property = target[contextPropertyName(eventName)];
785 // This might happen when the event bubbles
786 if (property.isUndefined())
787 return;
788 EventCallback *that = reinterpret_cast<EventCallback *>(property.as<intptr_t>());
789 that->m_fn(event);
790}
791
792std::string EventCallback::contextPropertyName(const std::string &eventName)
793{
794 return std::string("data-qtEventCallbackContext") + eventName;
795}
796
797EMSCRIPTEN_BINDINGS(qtStdwebCalback) {
798 emscripten::function("qtStdWebEventCallbackActivate", &EventCallback::activate);
799}
800
801namespace Promise {
802 void adoptPromise(emscripten::val promiseObject, PromiseCallbacks callbacks) {
803 validateCallbacks(callbacks);
804
805 WebPromiseManager::get()->adoptPromise(
806 std::move(promiseObject), std::move(callbacks));
807 }
808
809 void all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks) {
810 struct State {
811 std::map<int, emscripten::val> results;
812 int remainingThenCallbacks;
813 int remainingFinallyCallbacks;
814 };
815
816 validateCallbacks(callbacks);
817
818 auto state = std::make_shared<State>();
819 state->remainingThenCallbacks = state->remainingFinallyCallbacks = promises.size();
820
821 for (size_t i = 0; i < promises.size(); ++i) {
822 PromiseCallbacks individualPromiseCallback;
823 if (callbacks.thenFunc) {
824 individualPromiseCallback.thenFunc = [i, state, callbacks](emscripten::val partialResult) mutable {
825 state->results.emplace(i, std::move(partialResult));
826 if (!--(state->remainingThenCallbacks)) {
827 std::vector<emscripten::val> transformed;
828 for (auto& data : state->results) {
829 transformed.push_back(std::move(data.second));
830 }
831 callbacks.thenFunc(emscripten::val::array(std::move(transformed)));
832 }
833 };
834 }
835 if (callbacks.catchFunc) {
836 individualPromiseCallback.catchFunc = [state, callbacks](emscripten::val error) mutable {
837 callbacks.catchFunc(error);
838 };
839 }
840 individualPromiseCallback.finallyFunc = [state, callbacks]() mutable {
841 if (!--(state->remainingFinallyCallbacks)) {
842 if (callbacks.finallyFunc)
843 callbacks.finallyFunc();
844 // Explicitly reset here for verbosity, this would have been done automatically with the
845 // destruction of the adopted promise in WebPromiseManager.
846 state.reset();
847 }
848 };
849
850 adoptPromise(std::move(promises.at(i)), std::move(individualPromiseCallback));
851 }
852 }
853}
854
856{
857 static bool HaveAsyncify = jsHaveAsyncify();
858 return HaveAsyncify;
859}
860
862{
863 static bool HaveJspi = jsHaveJspi();
864 return HaveJspi;
865}
866
867std::shared_ptr<CancellationFlag>
868readDataTransfer(emscripten::val webDataTransfer, std::function<QVariant(QByteArray)> imageReader,
869 std::function<void(std::unique_ptr<QMimeData>)> onDone)
870{
871 return DataTransferReader::read(webDataTransfer, std::move(imageReader), std::move(onDone));
872}
873
874} // namespace qstdweb
875
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
qint64 size() const override
\reimp
Definition qfile.cpp:1156
\inmodule QtCore
Definition qmimedata.h:16
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
Definition qstring.h:127
static QString fromStdString(const std::string &s)
Definition qstring.h:1322
\inmodule QtCore
Definition qvariant.h:64
uint32_t byteLength() const
Definition qstdweb.cpp:494
emscripten::val val()
Definition qstdweb.cpp:502
uint32_t size() const
Definition qstdweb.cpp:513
emscripten::val val()
Definition qstdweb.cpp:535
std::string type() const
Definition qstdweb.cpp:561
Blob(const emscripten::val &blob)
Definition qstdweb.cpp:507
static Blob copyFrom(const char *buffer, uint32_t size, std::string mimeType)
Definition qstdweb.cpp:518
static void activate(emscripten::val event)
Definition qstdweb.cpp:780
FileList()=default
File item(int index) const
Definition qstdweb.cpp:602
int length() const
Definition qstdweb.cpp:597
emscripten::val val() const
Definition qstdweb.cpp:612
File operator[](int index) const
Definition qstdweb.cpp:607
void onAbort(const std::function< void(emscripten::val)> &onAbort)
Definition qstdweb.cpp:639
ArrayBuffer result() const
Definition qstdweb.cpp:617
void onLoad(const std::function< void(emscripten::val)> &onLoad)
Definition qstdweb.cpp:627
void readAsArrayBuffer(const Blob &blob) const
Definition qstdweb.cpp:622
void onError(const std::function< void(emscripten::val)> &onError)
Definition qstdweb.cpp:633
emscripten::val val()
Definition qstdweb.cpp:645
emscripten::val val()
Definition qstdweb.cpp:586
Blob slice(uint64_t begin, uint64_t end) const
Definition qstdweb.cpp:546
std::string name() const
Definition qstdweb.cpp:551
void stream(uint32_t offset, uint32_t length, char *buffer, std::function< void()> completed) const
Definition qstdweb.cpp:568
File()=default
std::string type() const
Definition qstdweb.cpp:581
uint64_t size() const
Definition qstdweb.cpp:556
void copyTo(char *destination) const
Definition qstdweb.cpp:706
static Uint8Array heap()
Definition qstdweb.cpp:650
void set(const Uint8Array &source)
Definition qstdweb.cpp:700
ArrayBuffer buffer() const
Definition qstdweb.cpp:690
QByteArray copyToQByteArray() const
Definition qstdweb.cpp:712
Uint8Array(const emscripten::val &uint8Array)
Definition qstdweb.cpp:656
static Uint8Array copyFrom(const char *buffer, uint32_t size)
Definition qstdweb.cpp:730
uint32_t length() const
Definition qstdweb.cpp:695
emscripten::val val()
Definition qstdweb.cpp:743
static void copy(char *destination, const Uint8Array &source)
Definition qstdweb.cpp:724
else opt state
[0]
Combined button and popup list for selecting options.
@ CaseSensitive
constexpr Initialization Uninitialized
void adoptPromise(emscripten::val promiseObject, PromiseCallbacks callbacks)
Definition qstdweb.cpp:802
void all(std::vector< emscripten::val > promises, PromiseCallbacks callbacks)
Definition qstdweb.cpp:809
double uint53_t
Definition qstdweb.cpp:37
std::shared_ptr< CancellationFlag > readDataTransfer(emscripten::val webDataTransfer, std::function< QVariant(QByteArray)> imageReader, std::function< void(std::unique_ptr< QMimeData >)> onDone)
Definition qstdweb.cpp:868
bool haveAsyncify()
Definition qstdweb.cpp:855
bool haveJspi()
Definition qstdweb.cpp:861
static void usePotentialyUnusedSymbols()
Definition qstdweb.cpp:22
EMSCRIPTEN_BINDINGS(qtStdwebCalback)
Definition qstdweb.cpp:797
static void * context
#define Q_LIKELY(x)
#define Q_FUNC_INFO
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)
EGLStreamKHR stream
const char * mimeType
INT_PTR intptr_t
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLuint buffer
GLenum type
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint name
GLsizei GLsizei GLchar * source
struct _cl_event * event
GLuint GLfloat * val
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
std::unique_ptr< ThunkPool::ThunkAllocation > allocation
Definition qstdweb.cpp:271
PromiseCallbacks callbacks
Definition qstdweb.cpp:270
#define THUNK(i)
Definition qstdweb.cpp:183
static constexpr size_t poolSize
Definition qstdweb.cpp:108
#define CALLBACK_BINDING(i)
Definition qstdweb.cpp:235
#define QStringLiteral(str)
ReturnedValue read(const char *data)
const char property[13]
Definition qwizard.cpp:101
QFile file
[0]
QMimeData * mimeData
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
std::function< void(emscripten::val)> thenFunc
Definition qstdweb_p.h:173
std::function< void()> finallyFunc
Definition qstdweb_p.h:175
std::function< void(emscripten::val)> catchFunc
Definition qstdweb_p.h:174