diff --git a/src/corelib/kernel/qcore_wasm.cpp b/src/corelib/kernel/qcore_wasm.cpp index d7135840fb..79b5b01cc1 100644 --- a/src/corelib/kernel/qcore_wasm.cpp +++ b/src/corelib/kernel/qcore_wasm.cpp @@ -42,4 +42,56 @@ emscripten::val QRectF::toDOMRect() const return emscripten::val::global("DOMRect").new_(left(), top(), width(), height()); } +/*! + Converts the \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string} \a + jsString to QString. Behavior is undefined if the provided parameter is not a string. + + \since 6.6 + \ingroup platform-type-conversions + + \sa toJsString() +*/ +QString QString::fromJsString(emscripten::val jsString) +{ + Q_ASSERT_X(jsString.isString(), Q_FUNC_INFO, "Passed object is not a string"); + + const double length = jsString["length"].as(); + + Q_ASSERT_X((double(uint64_t(length)) != double(uint64_t(length) - 1) + && double(uint64_t(length)) != double(uint64_t(length) + 1)) + || !std::numeric_limits::is_iec559, + Q_FUNC_INFO, "The floating-point length cannot precisely represent an integer"); + + constexpr int zeroTerminatorLength = 1; + const auto lengthOfUtf16 = (length + zeroTerminatorLength) * 2; + + Q_ASSERT_X((double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) - 1) + && double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) + 1)) + || !std::numeric_limits::is_iec559, + Q_FUNC_INFO, + "The floating-point lengthOfUtf16 cannot precisely represent an integer"); + + const QString result(uint64_t(length), Qt::Uninitialized); + + static const emscripten::val stringToUTF16(emscripten::val::module_property("stringToUTF16")); + stringToUTF16(jsString, emscripten::val(quintptr(result.data())), + emscripten::val(lengthOfUtf16)); + return result; +} + +/*! + Converts this object to an + \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string}. + + \since 6.6 + \ingroup platform-type-conversions + + \sa fromJsString() +*/ +emscripten::val QString::toJsString() const +{ + static const emscripten::val UTF16ToString(emscripten::val::module_property("UTF16ToString")); + return UTF16ToString(emscripten::val(quintptr(utf16()))); +} + QT_END_NAMESPACE diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index bde7041eef..91a9c86994 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -806,6 +806,11 @@ public: NSString *toNSString() const Q_DECL_NS_RETURNS_AUTORELEASED; #endif +#if defined(Q_OS_WASM) || defined(Q_QDOC) + static QString fromJsString(emscripten::val jsString); + emscripten::val toJsString() const; +#endif + inline bool isNull() const { return d->isNull(); } diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt index 15892617ca..8902597001 100644 --- a/src/plugins/platforms/wasm/CMakeLists.txt +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -31,7 +31,6 @@ qt_internal_add_plugin(QWasmIntegrationPlugin qwasmplatform.cpp qwasmplatform.h qwasmscreen.cpp qwasmscreen.h qwasmservices.cpp qwasmservices.h - qwasmstring.cpp qwasmstring.h qwasmstylepixmaps_p.h qwasmtheme.cpp qwasmtheme.h qwasmwindow.cpp qwasmwindow.h diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index d6343e9f6b..24523aacbf 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -3,7 +3,7 @@ #include "qwasmclipboard.h" #include "qwasmwindow.h" -#include "qwasmstring.h" + #include #include @@ -27,12 +27,11 @@ static void commonCopyEvent(val event) // doing it this way seems to sanitize the text better that calling data() like down below if (_mimes->hasText()) { - event["clipboardData"].call("setData", val("text/plain") - , QWasmString::fromQString(_mimes->text())); + event["clipboardData"].call("setData", val("text/plain"), + _mimes->text().toJsString()); } if (_mimes->hasHtml()) { - event["clipboardData"].call("setData", val("text/html") - , QWasmString::fromQString(_mimes->html())); + event["clipboardData"].call("setData", val("text/html"), _mimes->html().toJsString()); } for (auto mimetype : _mimes->formats()) { @@ -40,8 +39,8 @@ static void commonCopyEvent(val event) continue; QByteArray ba = _mimes->data(mimetype); if (!ba.isEmpty()) - event["clipboardData"].call("setData", QWasmString::fromQString(mimetype) - , val(ba.constData())); + event["clipboardData"].call("setData", mimetype.toJsString(), + val(ba.constData())); } event.call("preventDefault"); @@ -140,7 +139,7 @@ static void qClipboardPasteTo(val dataTransfer) || itemMimeType.contains("TEXT", Qt::CaseSensitive)) { break; } - const QString data = QWasmString::toQString( + const QString data = QString::fromJsString( clipboardData.call("getData", val(itemMimeType.toStdString()))); if (!data.isEmpty()) { @@ -329,12 +328,12 @@ void QWasmClipboard::writeToClipboardApi() // we have a blob, now create a ClipboardItem emscripten::val type = emscripten::val::array(); - type.set("type", val(QWasmString::fromQString(mimetype))); + type.set("type", mimetype.toJsString()); emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type); emscripten::val clipboardItemObject = emscripten::val::object(); - clipboardItemObject.set(val(QWasmString::fromQString(mimetype)), contentBlob); + clipboardItemObject.set(mimetype.toJsString(), contentBlob); val clipboardItemData = val::global("ClipboardItem").new_(clipboardItemObject); diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp index e159b8fe7d..2fc82bfcdc 100644 --- a/src/plugins/platforms/wasm/qwasmcursor.cpp +++ b/src/plugins/platforms/wasm/qwasmcursor.cpp @@ -3,7 +3,6 @@ #include "qwasmcursor.h" #include "qwasmscreen.h" -#include "qwasmstring.h" #include #include diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 3f3e8da3f1..395c9c3ee0 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -6,7 +6,6 @@ #include "qwasmcompositor.h" #include "qwasmintegration.h" #include "qwasmclipboard.h" -#include "qwasmstring.h" #include "qwasmcursor.h" #include #include diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 4c6f1b14c8..fa4ae981bb 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -11,7 +11,6 @@ #include "qwasmaccessibility.h" #include "qwasmservices.h" #include "qwasmoffscreensurface.h" -#include "qwasmstring.h" #include "qwasmwindow.h" #include "qwasmbackingstore.h" @@ -287,7 +286,8 @@ void QWasmIntegration::removeScreen(const emscripten::val &element) auto it = std::find_if(m_screens.begin(), m_screens.end(), [&] (const QPair &candidate) { return candidate.first.equals(element); }); if (it == m_screens.end()) { - qWarning() << "Attempting to remove non-existing screen for element" << QWasmString::toQString(element["id"]);; + qWarning() << "Attempting to remove non-existing screen for element" + << QString::fromJsString(element["id"]); return; } it->second->deleteScreen(); @@ -299,7 +299,8 @@ void QWasmIntegration::resizeScreen(const emscripten::val &element) auto it = std::find_if(m_screens.begin(), m_screens.end(), [&] (const QPair &candidate) { return candidate.first.equals(element); }); if (it == m_screens.end()) { - qWarning() << "Attempting to resize non-existing screen for element" << QWasmString::toQString(element["id"]);; + qWarning() << "Attempting to resize non-existing screen for element" + << QString::fromJsString(element["id"]); return; } it->second->updateQScreenAndCanvasRenderSize(); diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 3300449ae4..fd3d83b614 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -6,7 +6,6 @@ #include "qwasmeventtranslator.h" #include "qwasmcompositor.h" #include "qwasmintegration.h" -#include "qwasmstring.h" #include "qwasmcssstyle.h" #include @@ -194,7 +193,7 @@ qreal QWasmScreen::devicePixelRatio() const QString QWasmScreen::name() const { - return QWasmString::toQString(m_shadowContainer["id"]); + return QString::fromJsString(m_shadowContainer["id"]); } QPlatformCursor *QWasmScreen::cursor() const diff --git a/src/plugins/platforms/wasm/qwasmservices.cpp b/src/plugins/platforms/wasm/qwasmservices.cpp index b9f48090e1..f5fd4e4790 100644 --- a/src/plugins/platforms/wasm/qwasmservices.cpp +++ b/src/plugins/platforms/wasm/qwasmservices.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmservices.h" -#include "qwasmstring.h" #include #include @@ -13,8 +12,8 @@ QT_BEGIN_NAMESPACE bool QWasmServices::openUrl(const QUrl &url) { - emscripten::val jsUrl = QWasmString::fromQString(url.toString()); - emscripten::val::global("window").call("open", jsUrl, emscripten::val("_blank")); + emscripten::val::global("window").call("open", url.toString().toJsString(), + emscripten::val("_blank")); return true; } diff --git a/src/plugins/platforms/wasm/qwasmstring.cpp b/src/plugins/platforms/wasm/qwasmstring.cpp deleted file mode 100644 index 3de84afef3..0000000000 --- a/src/plugins/platforms/wasm/qwasmstring.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include "qwasmstring.h" - -QT_BEGIN_NAMESPACE - -using namespace emscripten; - -val QWasmString::fromQString(const QString &str) -{ - static const val UTF16ToString( - val::module_property("UTF16ToString")); - - auto ptr = quintptr(str.utf16()); - return UTF16ToString(val(ptr)); -} - -QString QWasmString::toQString(const val &v) -{ - QString result; - if (!v.isString()) - return result; - - static const val stringToUTF16( - val::module_property("stringToUTF16")); - static const val length("length"); - - int len = v[length].as(); - result.resize(len); - auto ptr = quintptr(result.utf16()); - stringToUTF16(v, val(ptr), val((len + 1) * 2)); - return result; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmstring.h b/src/plugins/platforms/wasm/qwasmstring.h deleted file mode 100644 index 62927ee93c..0000000000 --- a/src/plugins/platforms/wasm/qwasmstring.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#pragma once - -#include - -#include - -QT_BEGIN_NAMESPACE - -class QWasmString -{ -public: - static emscripten::val fromQString(const QString &str); - static QString toQString(const emscripten::val &v); -}; -QT_END_NAMESPACE - diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index aa229b5776..a8f577cfea 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -18,7 +18,6 @@ #include "qwasmcompositor.h" #include "qwasmevent.h" #include "qwasmeventdispatcher.h" -#include "qwasmstring.h" #include "qwasmaccessibility.h" #include diff --git a/tests/auto/corelib/text/qstring/CMakeLists.txt b/tests/auto/corelib/text/qstring/CMakeLists.txt index c3f8bbb717..d3db36e813 100644 --- a/tests/auto/corelib/text/qstring/CMakeLists.txt +++ b/tests/auto/corelib/text/qstring/CMakeLists.txt @@ -9,6 +9,9 @@ if(APPLE) list(APPEND tst_qstring_extra_libraries ${FWFoundation}) list(APPEND tst_qstring_extra_sources tst_qstring_mac.mm) endif() +if(WASM) + list(APPEND tst_qstring_extra_sources tst_qstring_wasm.cpp) +endif() foreach(test tst_qstring tst_qstring_restricted_ascii) qt_internal_add_test(${test} diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index aec2d2897a..2e733fc49b 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -541,6 +541,7 @@ private slots: #endif void STL(); void macTypes(); + void wasmTypes(); void isEmpty(); void isNull(); void nullness(); @@ -1344,6 +1345,16 @@ void tst_QString::macTypes() #endif } +void tst_QString::wasmTypes() +{ +#ifndef Q_OS_WASM + QSKIP("This is a WASM-only test"); +#else + extern void tst_QString_wasmTypes(); // in qcore_wasm.cpp + tst_QString_wasmTypes(); +#endif +} + void tst_QString::truncate() { QString nullStr; diff --git a/tests/auto/corelib/text/qstring/tst_qstring_wasm.cpp b/tests/auto/corelib/text/qstring/tst_qstring_wasm.cpp new file mode 100644 index 0000000000..df5ebddc96 --- /dev/null +++ b/tests/auto/corelib/text/qstring/tst_qstring_wasm.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include + +#include + +void tst_QString_wasmTypes() +{ + // QString <-> emscripten::val + { + QString qtString("test string"); + const emscripten::val jsString = qtString.toJsString(); + QString qtStringCopy(qtString); + qtString = qtString.toUpper(); // modify + QCOMPARE(QString::fromJsString(jsString), qtStringCopy); + } + { + QString longString; + for (uint64_t i = 0; i < 1000; ++i) + longString += "Lorem ipsum FTW"; + const emscripten::val jsString = longString.toJsString(); + QString qtStringCopy(longString); + longString = longString.toUpper(); // modify + QCOMPARE(QString::fromJsString(jsString), qtStringCopy); + } +}