summaryrefslogtreecommitdiffstats
path: root/src/corelib/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/platform')
-rw-r--r--src/corelib/platform/android/qandroidextras.cpp113
-rw-r--r--src/corelib/platform/android/qandroidnativeinterface.cpp62
-rw-r--r--src/corelib/platform/darwin/qdarwinpermissionplugin_bluetooth.mm6
-rw-r--r--src/corelib/platform/darwin/qdarwinpermissionplugin_calendar.mm22
-rw-r--r--src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm65
-rw-r--r--src/corelib/platform/darwin/qdarwinpermissionplugin_p_p.h4
-rw-r--r--src/corelib/platform/ios/PrivacyInfo.xcprivacy31
-rw-r--r--src/corelib/platform/wasm/qstdweb.cpp393
-rw-r--r--src/corelib/platform/wasm/qstdweb_p.h158
-rw-r--r--src/corelib/platform/windows/qcomobject_p.h127
-rw-r--r--src/corelib/platform/windows/qfactorycacheregistration_p.h2
-rw-r--r--src/corelib/platform/windows/qt_winrtbase_p.h34
12 files changed, 831 insertions, 186 deletions
diff --git a/src/corelib/platform/android/qandroidextras.cpp b/src/corelib/platform/android/qandroidextras.cpp
index 414374d1e0..aa0c3fd093 100644
--- a/src/corelib/platform/android/qandroidextras.cpp
+++ b/src/corelib/platform/android/qandroidextras.cpp
@@ -126,6 +126,8 @@ QAndroidBinder QAndroidParcelPrivate::readBinder() const
\l {https://developer.android.com/reference/android/os/Parcel.html}{Android Parcel}
methods.
+ \include qtcore.qdoc qtcoreprivate-usage
+
\since 6.2
*/
@@ -243,6 +245,8 @@ QJniObject QAndroidParcel::handle() const
\l {https://developer.android.com/reference/android/os/Binder.html}{Android Binder}
methods.
+ \include qtcore.qdoc qtcoreprivate-usage
+
\since 6.2
*/
@@ -383,6 +387,8 @@ QJniObject QAndroidBinder::handle() const
It is useful when you perform a QtAndroidPrivate::bindService operation.
+ \include qtcore.qdoc qtcoreprivate-usage
+
\since 6.2
*/
@@ -507,6 +513,8 @@ public:
Create a subclass of this class to be notified of the results when using the
\c QtAndroidPrivate::startActivity() and \c QtAndroidPrivate::startIntentSender() APIs.
+
+ \include qtcore.qdoc qtcoreprivate-usage
*/
/*!
@@ -604,6 +612,8 @@ public:
\l {https://developer.android.com/reference/android/app/Service.html}{Android Service}
methods.
+ \include qtcore.qdoc qtcoreprivate-usage
+
\since 6.2
*/
@@ -662,6 +672,45 @@ QAndroidBinder* QAndroidService::onBind(const QAndroidIntent &/*intent*/)
return nullptr;
}
+static jboolean onTransact(JNIEnv */*env*/, jclass /*cls*/, jlong id, jint code, jobject data,
+ jobject reply, jint flags)
+{
+ if (!id)
+ return false;
+
+ return reinterpret_cast<QAndroidBinder*>(id)->onTransact(
+ code, QAndroidParcel(data), QAndroidParcel(reply), QAndroidBinder::CallType(flags));
+}
+
+static void onServiceConnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name,
+ jobject service)
+{
+ if (!id)
+ return;
+
+ return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceConnected(
+ QJniObject(name).toString(), QAndroidBinder(service));
+}
+
+static void onServiceDisconnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name)
+{
+ if (!id)
+ return;
+
+ return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceDisconnected(
+ QJniObject(name).toString());
+}
+
+bool QtAndroidPrivate::registerExtrasNatives(QJniEnvironment &env)
+{
+ static const JNINativeMethod methods[] = {
+ {"onTransact", "(JILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void *)onTransact},
+ {"onServiceConnected", "(JLjava/lang/String;Landroid/os/IBinder;)V", (void *)onServiceConnected},
+ {"onServiceDisconnected", "(JLjava/lang/String;)V", (void *)onServiceDisconnected}
+ };
+
+ return env.registerNativeMethods("org/qtproject/qt/android/extras/QtNative", methods, 3);
+}
/*!
\class QAndroidIntent
@@ -674,6 +723,8 @@ QAndroidBinder* QAndroidService::onBind(const QAndroidIntent &/*intent*/)
\l {https://developer.android.com/reference/android/content/Intent.html}{Android Intent}
methods.
+ \include qtcore.qdoc qtcoreprivate-usage
+
\since 6.2
*/
@@ -799,6 +850,8 @@ QJniObject QAndroidIntent::handle() const
\brief The QtAndroidPrivate namespace provides miscellaneous functions
to aid Android development.
\inheaderfile QtCore/private/qandroidextras_p.h
+
+ \include qtcore.qdoc qtcoreprivate-usage
*/
/*!
@@ -1069,29 +1122,29 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject *obj, jint request
QFuture<QtAndroidPrivate::PermissionResult>
requestPermissionsInternal(const QStringList &permissions)
{
- QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
- promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
- QFuture<QtAndroidPrivate::PermissionResult> future = promise->future();
- promise->start();
-
// No mechanism to request permission for SDK version below 23, because
// permissions defined in the manifest are granted at install time.
if (QtAndroidPrivate::androidSdkVersion() < 23) {
- for (int i = 0; i < permissions.size(); ++i)
- promise->addResult(QtAndroidPrivate::checkPermission(permissions.at(i)).result(), i);
- promise->finish();
- return future;
+ QList<QtAndroidPrivate::PermissionResult> result;
+ result.reserve(permissions.size());
+ // ### can we kick off all checkPermission()s, and whenAll() collect results?
+ for (const QString &permission : permissions)
+ result.push_back(QtAndroidPrivate::checkPermission(permission).result());
+ return QtFuture::makeReadyRangeFuture(result);
}
- if (!QtAndroidPrivate::acquireAndroidDeadlockProtector()) {
- promise->addResult(QtAndroidPrivate::Denied);
- promise->finish();
- return future;
- }
+ if (!QtAndroidPrivate::acquireAndroidDeadlockProtector())
+ return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
+
+ QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
+ promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
+ QFuture<QtAndroidPrivate::PermissionResult> future = promise->future();
+ promise->start();
const int requestCode = nextRequestCode();
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
g_pendingPermissionRequests->insert(requestCode, promise);
+ locker.unlock();
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([permissions, requestCode] {
QJniEnvironment env;
@@ -1130,15 +1183,9 @@ QFuture<QtAndroidPrivate::PermissionResult>
QtAndroidPrivate::requestPermissions(const QStringList &permissions)
{
// avoid the uneccessary call and response to an empty permission string
- if (permissions.size() > 0)
- return requestPermissionsInternal(permissions);
-
- QPromise<QtAndroidPrivate::PermissionResult> promise;
- QFuture<QtAndroidPrivate::PermissionResult> future = promise.future();
- promise.start();
- promise.addResult(QtAndroidPrivate::Denied);
- promise.finish();
- return future;
+ if (permissions.isEmpty())
+ return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
+ return requestPermissionsInternal(permissions);
}
/*!
@@ -1152,25 +1199,18 @@ QtAndroidPrivate::requestPermissions(const QStringList &permissions)
QFuture<QtAndroidPrivate::PermissionResult>
QtAndroidPrivate::checkPermission(const QString &permission)
{
- QPromise<QtAndroidPrivate::PermissionResult> promise;
- QFuture<QtAndroidPrivate::PermissionResult> future = promise.future();
- promise.start();
-
- if (permission.size() > 0) {
+ QtAndroidPrivate::PermissionResult result = Denied;
+ if (!permission.isEmpty()) {
auto res = QJniObject::callStaticMethod<jint>(qtNativeClassName,
"checkSelfPermission",
"(Ljava/lang/String;)I",
QJniObject::fromString(permission).object());
- promise.addResult(resultFromAndroid(res));
- } else {
- promise.addResult(QtAndroidPrivate::Denied);
+ result = resultFromAndroid(res);
}
-
- promise.finish();
- return future;
+ return QtFuture::makeReadyValueFuture(result);
}
-bool QtAndroidPrivate::registerPermissionNatives()
+bool QtAndroidPrivate::registerPermissionNatives(QJniEnvironment &env)
{
if (QtAndroidPrivate::androidSdkVersion() < 23)
return true;
@@ -1180,8 +1220,9 @@ bool QtAndroidPrivate::registerPermissionNatives()
reinterpret_cast<void *>(sendRequestPermissionsResult)
}};
- QJniEnvironment env;
return env.registerNativeMethods(qtNativeClassName, methods, 1);
}
QT_END_NAMESPACE
+
+#include "moc_qandroidextras_p.cpp"
diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp
index 91b54a38e0..351893eb81 100644
--- a/src/corelib/platform/android/qandroidnativeinterface.cpp
+++ b/src/corelib/platform/android/qandroidnativeinterface.cpp
@@ -7,9 +7,13 @@
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/qjniobject.h>
#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
-#include <QtConcurrent/QtConcurrent>
+#include <QtCore/qfuture.h>
+#include <QtCore/qfuturewatcher.h>
#include <QtCore/qpromise.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qthreadpool.h>
#include <deque>
+#include <memory>
#endif
QT_BEGIN_NAMESPACE
@@ -17,8 +21,12 @@ QT_BEGIN_NAMESPACE
#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative";
-typedef std::pair<std::function<QVariant()>, QSharedPointer<QPromise<QVariant>>> RunnablePair;
-typedef std::deque<RunnablePair> PendingRunnables;
+struct PendingRunnable {
+ std::function<QVariant()> function;
+ std::shared_ptr<QPromise<QVariant>> promise;
+};
+
+using PendingRunnables = std::deque<PendingRunnable>;
Q_GLOBAL_STATIC(PendingRunnables, g_pendingRunnables);
Q_CONSTINIT static QBasicMutex g_pendingRunnablesMutex;
#endif
@@ -38,10 +46,10 @@ Q_CONSTINIT static QBasicMutex g_pendingRunnablesMutex;
QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication);
/*!
- \fn jobject QNativeInterface::QAndroidApplication::context()
+ \fn QJniObject QNativeInterface::QAndroidApplication::context()
- Returns the Android context as a \c jobject. The context is an \c Activity
- if the main activity object is valid. Otherwise, the context is a \c Service.
+ Returns the Android context as a \c QJniObject. The context is an \c Activity
+ if the most recently started activity object is valid. Otherwise, the context is a \c Service.
\since 6.2
*/
@@ -60,7 +68,7 @@ QtJniTypes::Context QNativeInterface::QAndroidApplication::context()
*/
bool QNativeInterface::QAndroidApplication::isActivityContext()
{
- return QtAndroidPrivate::activity();
+ return QtAndroidPrivate::activity().isValid();
}
/*!
@@ -86,8 +94,7 @@ int QNativeInterface::QAndroidApplication::sdkVersion()
*/
void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
{
- QJniObject::callStaticMethod<void>("org/qtproject/qt/android/QtNative",
- "hideSplashScreen", "(I)V", duration);
+ QtAndroidPrivate::activity().callMethod<void>("hideSplashScreen", duration);
}
/*!
@@ -155,12 +162,12 @@ QFuture<QVariant> QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
const std::function<QVariant()> &runnable,
const QDeadlineTimer timeout)
{
- QSharedPointer<QPromise<QVariant>> promise(new QPromise<QVariant>());
+ auto promise = std::make_shared<QPromise<QVariant>>();
QFuture<QVariant> future = promise->future();
promise->start();
- (void) QtConcurrent::run([=, &future]() {
- if (!timeout.isForever()) {
+ if (!timeout.isForever()) {
+ QThreadPool::globalInstance()->start([=]() mutable {
QEventLoop loop;
QTimer::singleShot(timeout.remainingTime(), &loop, [&]() {
future.cancel();
@@ -176,12 +183,24 @@ QFuture<QVariant> QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
loop.quit();
});
watcher.setFuture(future);
+
+ // we're going to sleep, make sure we don't block
+ // QThreadPool::globalInstance():
+
+ QThreadPool::globalInstance()->releaseThread();
+ const auto sg = qScopeGuard([] {
+ QThreadPool::globalInstance()->reserveThread();
+ });
loop.exec();
- }
- });
+ });
+ }
QMutexLocker locker(&g_pendingRunnablesMutex);
- g_pendingRunnables->push_back(std::pair(runnable, promise));
+#ifdef __cpp_aggregate_paren_init
+ g_pendingRunnables->emplace_back(runnable, std::move(promise));
+#else
+ g_pendingRunnables->push_back({runnable, std::move(promise)});
+#endif
locker.unlock();
QJniObject::callStaticMethod<void>(qtNativeClassName,
@@ -199,24 +218,23 @@ static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
if (g_pendingRunnables->empty())
break;
- std::pair pair = std::move(g_pendingRunnables->front());
+ PendingRunnable r = std::move(g_pendingRunnables->front());
g_pendingRunnables->pop_front();
locker.unlock();
// run the runnable outside the sync block!
- auto promise = pair.second;
- if (!promise->isCanceled())
- promise->addResult(pair.first());
- promise->finish();
+ if (!r.promise->isCanceled())
+ r.promise->addResult(r.function());
+ r.promise->finish();
}
}
#endif
-bool QtAndroidPrivate::registerNativeInterfaceNatives()
+bool QtAndroidPrivate::registerNativeInterfaceNatives(QJniEnvironment &env)
{
#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
const JNINativeMethod methods = {"runPendingCppRunnables", "()V", (void *)runPendingCppRunnables};
- return QJniEnvironment().registerNativeMethods(qtNativeClassName, &methods, 1);
+ return env.registerNativeMethods(qtNativeClassName, &methods, 1);
#else
return true;
#endif
diff --git a/src/corelib/platform/darwin/qdarwinpermissionplugin_bluetooth.mm b/src/corelib/platform/darwin/qdarwinpermissionplugin_bluetooth.mm
index 01fb638283..0cd375561f 100644
--- a/src/corelib/platform/darwin/qdarwinpermissionplugin_bluetooth.mm
+++ b/src/corelib/platform/darwin/qdarwinpermissionplugin_bluetooth.mm
@@ -31,7 +31,8 @@
- (Qt::PermissionStatus)currentStatus
{
- switch (CBCentralManager.authorization) {
+ auto status = CBCentralManager.authorization;
+ switch (status) {
case CBManagerAuthorizationNotDetermined:
return Qt::PermissionStatus::Undetermined;
case CBManagerAuthorizationRestricted:
@@ -41,7 +42,8 @@
return Qt::PermissionStatus::Granted;
}
- Q_UNREACHABLE();
+ qCWarning(lcPermissions) << "Unknown permission status" << status << "detected in" << self;
+ return Qt::PermissionStatus::Denied;
}
- (void)requestPermission:(QPermission)permission withCallback:(PermissionCallback)callback
diff --git a/src/corelib/platform/darwin/qdarwinpermissionplugin_calendar.mm b/src/corelib/platform/darwin/qdarwinpermissionplugin_calendar.mm
index 79a85ef3d2..a3eddd6d8f 100644
--- a/src/corelib/platform/darwin/qdarwinpermissionplugin_calendar.mm
+++ b/src/corelib/platform/darwin/qdarwinpermissionplugin_calendar.mm
@@ -5,8 +5,6 @@
#include <EventKit/EventKit.h>
-QT_DEFINE_PERMISSION_STATUS_CONVERTER(EKAuthorizationStatus);
-
@interface QDarwinCalendarPermissionHandler ()
@property (nonatomic, retain) EKEventStore *eventStore;
@end
@@ -20,8 +18,24 @@ QT_DEFINE_PERMISSION_STATUS_CONVERTER(EKAuthorizationStatus);
- (Qt::PermissionStatus)currentStatus
{
- const auto status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
- return nativeStatusToQtStatus(status);
+ auto status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
+ switch (status) {
+ case EKAuthorizationStatusNotDetermined:
+ return Qt::PermissionStatus::Undetermined;
+ case EKAuthorizationStatusRestricted:
+ case EKAuthorizationStatusDenied:
+ return Qt::PermissionStatus::Denied;
+ case EKAuthorizationStatusAuthorized:
+ return Qt::PermissionStatus::Granted;
+#if QT_MACOS_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(140000, 170000)
+ case EKAuthorizationStatusWriteOnly:
+ // FIXME: Add WriteOnly AccessMode
+ return Qt::PermissionStatus::Denied;
+#endif
+ }
+
+ qCWarning(lcPermissions) << "Unknown permission status" << status << "detected in" << self;
+ return Qt::PermissionStatus::Denied;
}
- (QStringList)usageDescriptionsFor:(QPermission)permission
diff --git a/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm b/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm
index 5414e97cbf..1d32c0fcac 100644
--- a/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm
+++ b/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm
@@ -55,7 +55,7 @@ struct PermissionRequest
- (Qt::PermissionStatus)checkPermission:(QPermission)permission
{
- const auto locationPermission = permission.data<QLocationPermission>();
+ const auto locationPermission = *permission.value<QLocationPermission>();
auto status = [self authorizationStatus:locationPermission];
if (status != Qt::PermissionStatus::Granted)
@@ -66,24 +66,39 @@ struct PermissionRequest
- (Qt::PermissionStatus)authorizationStatus:(QLocationPermission)permission
{
- switch ([self authorizationStatus]) {
+ NSString *bundleIdentifier = NSBundle.mainBundle.bundleIdentifier;
+ if (!bundleIdentifier || !bundleIdentifier.length) {
+ qCWarning(lcLocationPermission) << "Missing bundle identifier"
+ << "in Info.plist. Can not use location permissions.";
+ return Qt::PermissionStatus::Denied;
+ }
+
+#if defined(Q_OS_VISIONOS)
+ if (permission.availability() == QLocationPermission::Always)
+ return Qt::PermissionStatus::Denied;
+#endif
+
+ auto status = [self authorizationStatus];
+ switch (status) {
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
return Qt::PermissionStatus::Denied;
case kCLAuthorizationStatusNotDetermined:
return Qt::PermissionStatus::Undetermined;
+#if !defined(Q_OS_VISIONOS)
case kCLAuthorizationStatusAuthorizedAlways:
return Qt::PermissionStatus::Granted;
-#ifdef Q_OS_IOS
+#endif
+#if defined(Q_OS_IOS) || defined(Q_OS_VISIONOS)
case kCLAuthorizationStatusAuthorizedWhenInUse:
- if (permission.availability() == QLocationPermission::WhenInUse)
- return Qt::PermissionStatus::Granted;
- else
- return Qt::PermissionStatus::Denied; // FIXME: Verify
+ if (permission.availability() == QLocationPermission::Always)
+ return Qt::PermissionStatus::Denied;
+ return Qt::PermissionStatus::Granted;
#endif
}
- Q_UNREACHABLE();
+ qCWarning(lcPermissions) << "Unknown permission status" << status << "detected in" << self;
+ return Qt::PermissionStatus::Denied;
}
- (CLAuthorizationStatus)authorizationStatus
@@ -93,7 +108,7 @@ struct PermissionRequest
return self.manager.authorizationStatus;
}
- return CLLocationManager.authorizationStatus;
+ return QT_IGNORE_DEPRECATIONS(CLLocationManager.authorizationStatus);
}
- (Qt::PermissionStatus)accuracyAuthorization:(QLocationPermission)permission
@@ -109,19 +124,24 @@ struct PermissionRequest
if (permission.accuracy() == QLocationPermission::Approximate)
return Qt::PermissionStatus::Granted;
else
- return Qt::PermissionStatus::Denied; // FIXME: Verify
+ return Qt::PermissionStatus::Denied;
}
- Q_UNREACHABLE();
+ qCWarning(lcPermissions) << "Unknown accuracy status" << status << "detected in" << self;
+ return Qt::PermissionStatus::Denied;
}
- (QStringList)usageDescriptionsFor:(QPermission)permission
{
+#if defined(Q_OS_MACOS)
+ return { "NSLocationUsageDescription" };
+#else // iOS 11 and above
QStringList usageDescriptions = { "NSLocationWhenInUseUsageDescription" };
- const auto locationPermission = permission.data<QLocationPermission>();
+ const auto locationPermission = *permission.value<QLocationPermission>();
if (locationPermission.availability() == QLocationPermission::Always)
- usageDescriptions << "NSLocationAlwaysUsageDescription";
+ usageDescriptions << "NSLocationAlwaysAndWhenInUseUsageDescription";
return usageDescriptions;
+#endif
}
- (void)requestPermission:(QPermission)permission withCallback:(PermissionCallback)callback
@@ -150,7 +170,7 @@ struct PermissionRequest
self.manager.delegate = self;
}
- const auto locationPermission = permission.data<QLocationPermission>();
+ const auto locationPermission = *permission.value<QLocationPermission>();
switch (locationPermission.availability()) {
case QLocationPermission::WhenInUse:
// The documentation specifies that requestWhenInUseAuthorization can
@@ -164,19 +184,32 @@ struct PermissionRequest
}
break;
case QLocationPermission::Always:
+#if defined(Q_OS_VISIONOS)
+ [self deliverResult]; // Not supported
+#else
// The documentation specifies that requestAlwaysAuthorization can only
// be called when the current authorization status is either undetermined,
// or authorized when in use.
switch ([self authorizationStatus]) {
case kCLAuthorizationStatusNotDetermined:
+ [self.manager requestAlwaysAuthorization];
+ break;
#ifdef Q_OS_IOS
case kCLAuthorizationStatusAuthorizedWhenInUse:
+ // Unfortunately when asking for AlwaysAuthorization when in
+ // the WhenInUse state, to "upgrade" the permission, the iOS
+ // location system will not give us a callback if the user
+ // denies the request, leaving us hanging without a way to
+ // respond to the permission request.
+ qCWarning(lcLocationPermission) << "QLocationPermission::WhenInUse"
+ << "can not be upgraded to QLocationPermission::Always on iOS."
+ << "Please request QLocationPermission::Always directly.";
+ Q_FALLTHROUGH();
#endif
- [self.manager requestAlwaysAuthorization];
- break;
default:
[self deliverResult];
}
+#endif
break;
}
}
diff --git a/src/corelib/platform/darwin/qdarwinpermissionplugin_p_p.h b/src/corelib/platform/darwin/qdarwinpermissionplugin_p_p.h
index 9e4bbe92de..649af06507 100644
--- a/src/corelib/platform/darwin/qdarwinpermissionplugin_p_p.h
+++ b/src/corelib/platform/darwin/qdarwinpermissionplugin_p_p.h
@@ -83,7 +83,9 @@ Qt::PermissionStatus nativeStatusToQtStatus(NativeStatus status)
case Converter::Undetermined:
return Qt::PermissionStatus::Undetermined;
}
- Q_UNREACHABLE();
+ qCWarning(lcPermissions) << "Unknown permission status" << status << "detected in"
+ << QT_STRINGIFY(QT_DARWIN_PERMISSION_PLUGIN);
+ return Qt::PermissionStatus::Denied;
}
} // namespace
diff --git a/src/corelib/platform/ios/PrivacyInfo.xcprivacy b/src/corelib/platform/ios/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..5f84a229a5
--- /dev/null
+++ b/src/corelib/platform/ios/PrivacyInfo.xcprivacy
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyTracking</key>
+ <false/>
+ <key>NSPrivacyCollectedDataTypes</key>
+ <array/>
+ <key>NSPrivacyTrackingDomains</key>
+ <array/>
+ <key>NSPrivacyAccessedAPITypes</key>
+ <array>
+ <dict>
+ <key>NSPrivacyAccessedAPIType</key>
+ <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
+ <key>NSPrivacyAccessedAPITypeReasons</key>
+ <array>
+ <string>0A2A.1</string> <!-- QFileInfo -->
+ </array>
+ </dict>
+ <dict>
+ <key>NSPrivacyAccessedAPIType</key>
+ <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
+ <key>NSPrivacyAccessedAPITypeReasons</key>
+ <array>
+ <string>85F4.1</string> <!-- QStorageInfo -->
+ </array>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp
index 69ed8fe34f..75e76a6806 100644
--- a/src/corelib/platform/wasm/qstdweb.cpp
+++ b/src/corelib/platform/wasm/qstdweb.cpp
@@ -5,9 +5,13 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>
+#include <QtCore/qmimedata.h>
+
#include <emscripten/bind.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
+#include <emscripten/threading.h>
+
#include <cstdint>
#include <iostream>
@@ -15,6 +19,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
namespace qstdweb {
static void usePotentialyUnusedSymbols()
@@ -28,12 +34,59 @@ static void usePotentialyUnusedSymbols()
// called at runtime.
volatile bool doIt = false;
if (doIt)
- emscripten_set_wheel_callback(NULL, 0, 0, NULL);
+ emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 0, NULL);
}
Q_CONSTRUCTOR_FUNCTION(usePotentialyUnusedSymbols)
typedef double uint53_t; // see Number.MAX_SAFE_INTEGER
namespace {
+// Reads file in chunks in order to avoid holding two copies in memory at the same time
+struct ChunkedFileReader
+{
+public:
+ static void read(File file, char *buffer, uint32_t offset, uint32_t end,
+ std::function<void()> onCompleted)
+ {
+ (new ChunkedFileReader(end, std::move(onCompleted), std::move(file)))
+ ->readNextChunk(offset, buffer);
+ }
+
+private:
+ ChunkedFileReader(uint32_t end, std::function<void()> onCompleted, File file)
+ : end(end), onCompleted(std::move(onCompleted)), file(std::move(file))
+ {
+ }
+
+ void readNextChunk(uint32_t chunkBegin, char *chunkBuffer)
+ {
+ // Copy current chunk from JS memory to Wasm memory
+ qstdweb::ArrayBuffer result = fileReader.result();
+ qstdweb::Uint8Array(result).copyTo(chunkBuffer);
+
+ // Read next chunk if not at buffer end
+ const uint32_t nextChunkBegin = std::min(chunkBegin + result.byteLength(), end);
+ if (nextChunkBegin == end) {
+ onCompleted();
+ delete this;
+ return;
+ }
+ char *nextChunkBuffer = chunkBuffer + result.byteLength();
+ fileReader.onLoad([this, nextChunkBegin, nextChunkBuffer](emscripten::val) {
+ readNextChunk(nextChunkBegin, nextChunkBuffer);
+ });
+ const uint32_t nextChunkEnd = std::min(nextChunkBegin + chunkSize, end);
+ qstdweb::Blob blob = file.slice(nextChunkBegin, nextChunkEnd);
+ fileReader.readAsArrayBuffer(blob);
+ }
+
+ static constexpr uint32_t chunkSize = 256 * 1024;
+
+ qstdweb::FileReader fileReader;
+ uint32_t end;
+ std::function<void()> onCompleted;
+ File file;
+};
+
enum class CallbackType {
Then,
Catch,
@@ -85,11 +138,12 @@ public:
"catch",
emscripten::val::module_property(thunkName(CallbackType::Catch, id()).data()));
}
- if (callbacks.finallyFunc) {
- target = target.call<val>(
- "finally",
- emscripten::val::module_property(thunkName(CallbackType::Finally, id()).data()));
- }
+ // Guarantee the invocation of at least one callback by always
+ // registering 'finally'. This is required by WebPromiseManager
+ // design
+ target = target.call<val>(
+ "finally", emscripten::val::module_property(
+ thunkName(CallbackType::Finally, id()).data()));
}
private:
@@ -270,25 +324,21 @@ void WebPromiseManager::promiseThunkCallback(int context, CallbackType type, ems
auto* promiseState = &m_promiseRegistry[context];
auto* callbacks = &promiseState->callbacks;
- bool expectingOtherCallbacks;
switch (type) {
case CallbackType::Then:
callbacks->thenFunc(result);
- // At this point, if there is no finally function, we are sure that the Catch callback won't be issued.
- expectingOtherCallbacks = !!callbacks->finallyFunc;
break;
case CallbackType::Catch:
callbacks->catchFunc(result);
- expectingOtherCallbacks = !!callbacks->finallyFunc;
break;
case CallbackType::Finally:
- callbacks->finallyFunc();
- expectingOtherCallbacks = false;
+ // Final callback may be empty, used solely for promise unregistration
+ if (callbacks->finallyFunc) {
+ callbacks->finallyFunc();
+ }
+ unregisterPromise(context);
break;
- }
-
- if (!expectingOtherCallbacks)
- unregisterPromise(context);
+ }
}
void WebPromiseManager::registerPromise(
@@ -314,13 +364,15 @@ void WebPromiseManager::adoptPromise(emscripten::val target, PromiseCallbacks ca
#if defined(QT_STATIC)
EM_JS(bool, jsHaveAsyncify, (), { return typeof Asyncify !== "undefined"; });
+EM_JS(bool, jsHaveJspi, (),
+ { return typeof Asyncify !== "undefined" && !!Asyncify.makeAsyncFunction && !!WebAssembly.Function; });
#else
bool jsHaveAsyncify() { return false; }
+bool jsHaveJspi() { return false; }
#endif
-
} // namespace
ArrayBuffer::ArrayBuffer(uint32_t size)
@@ -342,7 +394,12 @@ uint32_t ArrayBuffer::byteLength() const
return m_arrayBuffer["byteLength"].as<uint32_t>();
}
-emscripten::val ArrayBuffer::val()
+ArrayBuffer ArrayBuffer::slice(uint32_t begin, uint32_t end) const
+{
+ return ArrayBuffer(m_arrayBuffer.call<emscripten::val>("slice", begin, end));
+}
+
+emscripten::val ArrayBuffer::val() const
{
return m_arrayBuffer;
}
@@ -353,24 +410,55 @@ Blob::Blob(const emscripten::val &blob)
}
+Blob Blob::fromArrayBuffer(const ArrayBuffer &arrayBuffer)
+{
+ auto array = emscripten::val::array();
+ array.call<void>("push", arrayBuffer.val());
+ return Blob(emscripten::val::global("Blob").new_(array));
+}
+
uint32_t Blob::size() const
{
return m_blob["size"].as<uint32_t>();
}
-// Copies content from the given buffer into a Blob object
-Blob Blob::copyFrom(const char *buffer, uint32_t size)
+Blob Blob::copyFrom(const char *buffer, uint32_t size, std::string mimeType)
{
Uint8Array contentCopy = Uint8Array::copyFrom(buffer, size);
emscripten::val contentArray = emscripten::val::array();
contentArray.call<void>("push", contentCopy.val());
emscripten::val type = emscripten::val::object();
- type.set("type","application/octet-stream");
+ type.set("type", std::move(mimeType));
return Blob(emscripten::val::global("Blob").new_(contentArray, type));
}
-emscripten::val Blob::val()
+// Copies content from the given buffer into a Blob object
+Blob Blob::copyFrom(const char *buffer, uint32_t size)
+{
+ return copyFrom(buffer, size, "application/octet-stream");
+}
+
+Blob Blob::slice(uint32_t begin, uint32_t end) const
+{
+ return Blob(m_blob.call<emscripten::val>("slice", begin, end));
+}
+
+ArrayBuffer Blob::arrayBuffer_sync() const
+{
+ QEventLoop loop;
+ emscripten::val buffer;
+ qstdweb::Promise::make(m_blob, QStringLiteral("arrayBuffer"), {
+ .thenFunc = [&loop, &buffer](emscripten::val arrayBuffer) {
+ buffer = arrayBuffer;
+ loop.quit();
+ }
+ });
+ loop.exec();
+ return ArrayBuffer(buffer);
+}
+
+emscripten::val Blob::val() const
{
return m_blob;
}
@@ -381,6 +469,17 @@ File::File(const emscripten::val &file)
}
+File::~File() = default;
+
+File::File(const File &other) = default;
+
+File::File(File &&other) = default;
+
+File &File::operator=(const File &other) = default;
+
+File &File::operator=(File &&other) = default;
+
+
Blob File::slice(uint64_t begin, uint64_t end) const
{
return Blob(m_file.call<emscripten::val>("slice", uint53_t(begin), uint53_t(end)));
@@ -403,47 +502,17 @@ std::string Blob::type() const
// Streams partial file content into the given buffer asynchronously. The completed
// callback is called on completion.
-void File::stream(uint32_t offset, uint32_t length, char *buffer, const std::function<void ()> &completed) const
+void File::stream(uint32_t offset, uint32_t length, char *buffer,
+ std::function<void()> completed) const
{
- // Read file in chunks in order to avoid holding two copies in memory at the same time
- const uint32_t chunkSize = 256 * 1024;
- const uint32_t end = offset + length;
- // assert end < file.size
- auto fileReader = std::make_shared<qstdweb::FileReader>();
-
- // "this" is valid now, but may not be by the time the chunkCompleted callback
- // below is made. Make a copy of the file handle.
- const File fileHandle = *this;
- auto chunkCompleted = std::make_shared<std::function<void (uint32_t, char *buffer)>>();
- *chunkCompleted = [=](uint32_t chunkBegin, char *chunkBuffer) mutable {
-
- // Copy current chunk from JS memory to Wasm memory
- qstdweb::ArrayBuffer result = fileReader->result();
- qstdweb::Uint8Array(result).copyTo(chunkBuffer);
-
- // Read next chunk if not at buffer end
- uint32_t nextChunkBegin = std::min(chunkBegin + result.byteLength(), end);
- uint32_t nextChunkEnd = std::min(nextChunkBegin + chunkSize, end);
- if (nextChunkBegin == end) {
- completed();
- chunkCompleted.reset();
- return;
- }
- char *nextChunkBuffer = chunkBuffer + result.byteLength();
- fileReader->onLoad([=](emscripten::val) { (*chunkCompleted)(nextChunkBegin, nextChunkBuffer); });
- qstdweb::Blob blob = fileHandle.slice(nextChunkBegin, nextChunkEnd);
- fileReader->readAsArrayBuffer(blob);
- };
-
- // Read first chunk. First iteration is a dummy iteration with no available data.
- (*chunkCompleted)(offset, buffer);
+ ChunkedFileReader::read(*this, buffer, offset, offset + length, std::move(completed));
}
// Streams file content into the given buffer asynchronously. The completed
// callback is called on completion.
-void File::stream(char *buffer, const std::function<void ()> &completed) const
+void File::stream(char *buffer, std::function<void()> completed) const
{
- stream(0, size(), buffer, completed);
+ stream(0, size(), buffer, std::move(completed));
}
std::string File::type() const
@@ -451,11 +520,27 @@ std::string File::type() const
return m_file["type"].as<std::string>();
}
-emscripten::val File::val()
+emscripten::val File::val() const
{
return m_file;
}
+FileUrlRegistration::FileUrlRegistration(File file)
+{
+ m_path = QString::fromStdString(emscripten::val::global("window")["URL"].call<std::string>(
+ "createObjectURL", file.file()));
+}
+
+FileUrlRegistration::~FileUrlRegistration()
+{
+ emscripten::val::global("window")["URL"].call<void>("revokeObjectURL",
+ emscripten::val(m_path.toStdString()));
+}
+
+FileUrlRegistration::FileUrlRegistration(FileUrlRegistration &&other) = default;
+
+FileUrlRegistration &FileUrlRegistration::operator=(FileUrlRegistration &&other) = default;
+
FileList::FileList(const emscripten::val &fileList)
:m_fileList(fileList)
{
@@ -494,20 +579,23 @@ void FileReader::readAsArrayBuffer(const Blob &blob) const
void FileReader::onLoad(const std::function<void(emscripten::val)> &onLoad)
{
- m_onLoad.reset(new EventCallback(m_fileReader, "load", onLoad));
+ m_onLoad.reset();
+ m_onLoad = std::make_unique<EventCallback>(m_fileReader, "load", onLoad);
}
void FileReader::onError(const std::function<void(emscripten::val)> &onError)
{
- m_onError.reset(new EventCallback(m_fileReader, "error", onError));
+ m_onError.reset();
+ m_onError = std::make_unique<EventCallback>(m_fileReader, "error", onError);
}
void FileReader::onAbort(const std::function<void(emscripten::val)> &onAbort)
{
- m_onAbort.reset(new EventCallback(m_fileReader, "abort", onAbort));
+ m_onAbort.reset();
+ m_onAbort = std::make_unique<EventCallback>(m_fileReader, "abort", onAbort);
}
-emscripten::val FileReader::val()
+emscripten::val FileReader::val() const
{
return m_fileReader;
}
@@ -567,6 +655,13 @@ void Uint8Array::set(const Uint8Array &source)
m_uint8Array.call<void>("set", source.m_uint8Array); // copies source content
}
+Uint8Array Uint8Array::subarray(uint32_t begin, uint32_t end)
+{
+ // Note: using uint64_t here errors with "Cannot convert a BigInt value to a number"
+ // (see JS BigInt and Number types). Use uint32_t for now.
+ return Uint8Array(m_uint8Array.call<emscripten::val>("subarray", begin, end));
+}
+
// Copies the Uint8Array content to a destination on the heap
void Uint8Array::copyTo(char *destination) const
{
@@ -605,7 +700,7 @@ Uint8Array Uint8Array::copyFrom(const QByteArray &buffer)
return copyFrom(buffer.constData(), buffer.size());
}
-emscripten::val Uint8Array::val()
+emscripten::val Uint8Array::val() const
{
return m_uint8Array;
}
@@ -620,45 +715,45 @@ emscripten::val Uint8Array::constructor_()
return emscripten::val::global("Uint8Array");
}
+class EventListener {
+public:
+ EventListener(uintptr_t handler)
+ :m_handler(handler)
+ {
+
+ }
+
+ // Special function - addEventListender() allows adding an object with a
+ // handleEvent() function which eceives the event.
+ void handleEvent(emscripten::val event) {
+ auto handlerPtr = reinterpret_cast<std::function<void(emscripten::val)> *>(m_handler);
+ (*handlerPtr)(event);
+ }
+
+ uintptr_t m_handler;
+};
+
// Registers a callback function for a named event on the given element. The event
// name must be the name as returned by the Event.type property: e.g. "load", "error".
EventCallback::~EventCallback()
{
- // Clean up if this instance's callback is still installed on the element
- if (m_element[contextPropertyName(m_eventName).c_str()].as<intptr_t>() == intptr_t(this)) {
- m_element.set(contextPropertyName(m_eventName).c_str(), emscripten::val::undefined());
- m_element.set((std::string("on") + m_eventName).c_str(), emscripten::val::undefined());
- }
+ m_element.call<void>("removeEventListener", m_eventName, m_eventListener);
}
-EventCallback::EventCallback(emscripten::val element, const std::string &name, const std::function<void(emscripten::val)> &fn)
+EventCallback::EventCallback(emscripten::val element, const std::string &name, const std::function<void(emscripten::val)> &handler)
:m_element(element)
,m_eventName(name)
- ,m_fn(fn)
-{
- m_element.set(contextPropertyName(m_eventName).c_str(), emscripten::val(intptr_t(this)));
- m_element.set((std::string("on") + m_eventName).c_str(), emscripten::val::module_property("qtStdWebEventCallbackActivate"));
-}
-
-void EventCallback::activate(emscripten::val event)
+ ,m_handler(std::make_unique<std::function<void(emscripten::val)>>(handler))
{
- emscripten::val target = event["target"];
- std::string eventName = event["type"].as<std::string>();
- emscripten::val property = target[contextPropertyName(eventName)];
- // This might happen when the event bubbles
- if (property.isUndefined())
- return;
- EventCallback *that = reinterpret_cast<EventCallback *>(property.as<intptr_t>());
- that->m_fn(event);
-}
-
-std::string EventCallback::contextPropertyName(const std::string &eventName)
-{
- return std::string("data-qtEventCallbackContext") + eventName;
+ uintptr_t handlerUint = reinterpret_cast<uintptr_t>(m_handler.get()); // FIXME: pass pointer directly instead
+ m_eventListener = emscripten::val::module_property("QtEventListener").new_(handlerUint);
+ m_element.call<void>("addEventListener", m_eventName, m_eventListener);
}
EMSCRIPTEN_BINDINGS(qtStdwebCalback) {
- emscripten::function("qtStdWebEventCallbackActivate", &EventCallback::activate);
+ emscripten::class_<EventListener>("QtEventListener")
+ .constructor<uintptr_t>()
+ .function("handleEvent", &EventListener::handleEvent);
}
namespace Promise {
@@ -715,12 +810,128 @@ namespace Promise {
}
}
+// Asyncify and thread blocking: Normally, it's not possible to block the main
+// thread, except if asyncify is enabled. Secondary threads can always block.
+//
+// haveAsyncify(): returns true if the main thread can block on QEventLoop::exec(),
+// if either asyncify 1 or 2 (JSPI) is available.
+//
+// haveJspi(): returns true if asyncify 2 (JSPI) is available.
+//
+// canBlockCallingThread(): returns true if the calling thread can block on
+// QEventLoop::exec(), using either asyncify or as a seconarday thread.
+bool haveJspi()
+{
+ static bool HaveJspi = jsHaveJspi();
+ return HaveJspi;
+}
+
bool haveAsyncify()
{
- static bool HaveAsyncify = jsHaveAsyncify();
+ static bool HaveAsyncify = jsHaveAsyncify() || haveJspi();
return HaveAsyncify;
}
+bool canBlockCallingThread()
+{
+ return haveAsyncify() || !emscripten_is_main_runtime_thread();
+}
+
+BlobIODevice::BlobIODevice(Blob blob)
+ : m_blob(blob)
+{
+
+}
+
+bool BlobIODevice::open(QIODevice::OpenMode mode)
+{
+ if (mode.testFlag(QIODevice::WriteOnly))
+ return false;
+ return QIODevice::open(mode);
+}
+
+bool BlobIODevice::isSequential() const
+{
+ return false;
+}
+
+qint64 BlobIODevice::size() const
+{
+ return m_blob.size();
+}
+
+bool BlobIODevice::seek(qint64 pos)
+{
+ if (pos >= size())
+ return false;
+ return QIODevice::seek(pos);
+}
+
+qint64 BlobIODevice::readData(char *data, qint64 maxSize)
+{
+ uint64_t begin = QIODevice::pos();
+ uint64_t end = std::min<uint64_t>(begin + maxSize, size());
+ uint64_t size = end - begin;
+ if (size > 0) {
+ qstdweb::ArrayBuffer buffer = m_blob.slice(begin, end).arrayBuffer_sync();
+ qstdweb::Uint8Array(buffer).copyTo(data);
+ }
+ return size;
+}
+
+qint64 BlobIODevice::writeData(const char *, qint64)
+{
+ Q_UNREACHABLE();
+}
+
+Uint8ArrayIODevice::Uint8ArrayIODevice(Uint8Array array)
+ : m_array(array)
+{
+
+}
+
+bool Uint8ArrayIODevice::open(QIODevice::OpenMode mode)
+{
+ return QIODevice::open(mode);
+}
+
+bool Uint8ArrayIODevice::isSequential() const
+{
+ return false;
+}
+
+qint64 Uint8ArrayIODevice::size() const
+{
+ return m_array.length();
+}
+
+bool Uint8ArrayIODevice::seek(qint64 pos)
+{
+ if (pos >= size())
+ return false;
+ return QIODevice::seek(pos);
+}
+
+qint64 Uint8ArrayIODevice::readData(char *data, qint64 maxSize)
+{
+ uint64_t begin = QIODevice::pos();
+ uint64_t end = std::min<uint64_t>(begin + maxSize, size());
+ uint64_t size = end - begin;
+ if (size > 0)
+ m_array.subarray(begin, end).copyTo(data);
+ return size;
+}
+
+qint64 Uint8ArrayIODevice::writeData(const char *data, qint64 maxSize)
+{
+ uint64_t begin = QIODevice::pos();
+ uint64_t end = std::min<uint64_t>(begin + maxSize, size());
+ uint64_t size = end - begin;
+ if (size > 0)
+ m_array.subarray(begin, end).set(Uint8Array(data, size));
+ return size;
+}
+
} // namespace qstdweb
QT_END_NAMESPACE
diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h
index b3fc8a95f7..a3b5bd5b6b 100644
--- a/src/corelib/platform/wasm/qstdweb_p.h
+++ b/src/corelib/platform/wasm/qstdweb_p.h
@@ -16,15 +16,28 @@
//
#include <private/qglobal_p.h>
+#include <QtCore/qglobal.h>
+#include "QtCore/qhash.h"
+#include "QtCore/qiodevice.h"
+
#include <emscripten/val.h>
+
#include <cstdint>
#include <functional>
-#include "initializer_list"
-#include <QtCore/qglobal.h>
-#include "QtCore/qhash.h"
+#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)
QT_BEGIN_NAMESPACE
+class QMimeData;
+
namespace qstdweb {
extern const char makeContextfulPromiseFunctionName[];
@@ -46,7 +59,8 @@ namespace qstdweb {
explicit ArrayBuffer(uint32_t size);
explicit ArrayBuffer(const emscripten::val &arrayBuffer);
uint32_t byteLength() const;
- emscripten::val val();
+ ArrayBuffer slice(uint32_t begin, uint32_t end) const;
+ emscripten::val val() const;
private:
friend class Uint8Array;
@@ -56,9 +70,13 @@ namespace qstdweb {
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);
- emscripten::val val();
+ Blob slice(uint32_t begin, uint32_t end) const;
+ ArrayBuffer arrayBuffer_sync() const;
+ emscripten::val val() const;
std::string type() const;
private:
@@ -70,19 +88,49 @@ namespace qstdweb {
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, const std::function<void ()> &completed) const;
- void stream(char *buffer, const std::function<void ()> &completed) const;
- emscripten::val val();
+ 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;
@@ -105,7 +153,7 @@ namespace qstdweb {
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();
+ emscripten::val val() const;
private:
emscripten::val m_fileReader = emscripten::val::global("FileReader").new_();
@@ -126,6 +174,7 @@ namespace qstdweb {
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;
@@ -133,7 +182,7 @@ namespace qstdweb {
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();
+ emscripten::val val() const;
private:
static emscripten::val heap_();
@@ -150,13 +199,12 @@ namespace qstdweb {
EventCallback& operator=(EventCallback const&) = delete;
EventCallback(emscripten::val element, const std::string &name,
const std::function<void(emscripten::val)> &fn);
- static void activate(emscripten::val event);
private:
- static std::string contextPropertyName(const std::string &eventName);
emscripten::val m_element = emscripten::val::undefined();
std::string m_eventName;
- std::function<void(emscripten::val)> m_fn;
+ std::unique_ptr<std::function<void(emscripten::val)>> m_handler;
+ emscripten::val m_eventListener = emscripten::val::undefined();
};
struct PromiseCallbacks
@@ -187,6 +235,46 @@ namespace qstdweb {
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;
+ };
+
inline emscripten::val window()
{
static emscripten::val savedWindow = emscripten::val::global("window");
@@ -194,6 +282,50 @@ namespace qstdweb {
}
bool haveAsyncify();
+ bool Q_CORE_EXPORT haveJspi();
+ bool canBlockCallingThread();
+
+ struct CancellationFlag
+ {
+ };
+
+#if QT_CONFIG(thread)
+ template<class T>
+ T proxyCall(std::function<T()> task, emscripten::ProxyingQueue *queue)
+ {
+ T result;
+ queue->proxySync(emscripten_main_runtime_thread_id(),
+ [task, result = &result]() { *result = task(); });
+ return result;
+ }
+
+ template<>
+ inline void proxyCall<void>(std::function<void()> task, emscripten::ProxyingQueue *queue)
+ {
+ queue->proxySync(emscripten_main_runtime_thread_id(), task);
+ }
+
+ template<class T>
+ T runTaskOnMainThread(std::function<T()> task, emscripten::ProxyingQueue *queue)
+ {
+ return emscripten_is_main_runtime_thread() ? task() : proxyCall<T>(std::move(task), queue);
+ }
+
+ template<class T>
+ T runTaskOnMainThread(std::function<T()> task)
+ {
+ emscripten::ProxyingQueue singleUseQueue;
+ return runTaskOnMainThread<T>(task, &singleUseQueue);
+ }
+
+#else
+ template<class T>
+ T runTaskOnMainThread(std::function<T()> task)
+ {
+ return task();
+ }
+#endif // QT_CONFIG(thread)
+
}
QT_END_NAMESPACE
diff --git a/src/corelib/platform/windows/qcomobject_p.h b/src/corelib/platform/windows/qcomobject_p.h
new file mode 100644
index 0000000000..8f27a18ff6
--- /dev/null
+++ b/src/corelib/platform/windows/qcomobject_p.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 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
+
+#ifndef QCOMOBJECT_P_H
+#define QCOMOBJECT_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 <QtCore/private/qglobal_p.h>
+
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
+
+# include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+template <typename... TInterfaces>
+struct QComObjectTraits
+{
+ static constexpr bool isGuidOf(REFIID riid) noexcept
+ {
+ return ((riid == __uuidof(TInterfaces)) || ...);
+ }
+};
+
+} // namespace QtPrivate
+
+// NOTE: In order to be able to query the intermediate interface, i.e. the one you do not specify in
+// QComObject interface list (TFirstInterface, TInterfaces) but that is a base for any of them
+// (except IUnknown) you need to provide an explicit specialization of function
+// QComObjectTraits<...>::isGuidOf for that type. For example, if you want to inherit interface
+// IMFSampleGrabberSinkCallback which inherits IMFClockStateSink and you want to be able to query
+// the latter one you need to provide this explicit specialization:
+//
+// class SinkCallback : public QComObject<IMFSampleGrabberSinkCallback>
+// {
+// ...
+// };
+//
+// namespace QtPrivate {
+//
+// template <>
+// struct QComObjectTraits<IMFSampleGrabberSinkCallback>
+// {
+// static constexpr bool isGuidOf(REFIID riid) noexcept
+// {
+// return QComObjectTraits<IMFSampleGrabberSinkCallback, IMFClockStateSink>::isGuidOf(riid);
+// }
+// };
+//
+// }
+
+template <typename TFirstInterface, typename... TAdditionalInterfaces>
+class QComObject : public TFirstInterface, public TAdditionalInterfaces...
+{
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (riid == __uuidof(IUnknown)) {
+ *ppvObject = static_cast<IUnknown *>(static_cast<TFirstInterface *>(this));
+ AddRef();
+
+ return S_OK;
+ }
+
+ return tryQueryInterface<TFirstInterface, TAdditionalInterfaces...>(riid, ppvObject);
+ }
+
+ STDMETHODIMP_(ULONG) AddRef() override { return ++m_referenceCount; }
+
+ STDMETHODIMP_(ULONG) Release() override
+ {
+ const LONG referenceCount = --m_referenceCount;
+ if (referenceCount == 0)
+ delete this;
+
+ return referenceCount;
+ }
+
+protected:
+ QComObject() = default;
+
+ // Destructor is not public. Caller should call Release.
+ // Derived class should make its destructor private to force this behavior.
+ virtual ~QComObject() = default;
+
+private:
+ template <typename TInterface, typename... TRest>
+ HRESULT tryQueryInterface(REFIID riid, void **ppvObject)
+ {
+ if (QtPrivate::QComObjectTraits<TInterface>::isGuidOf(riid)) {
+ *ppvObject = static_cast<TInterface *>(this);
+ AddRef();
+
+ return S_OK;
+ }
+
+ if constexpr (sizeof...(TRest) > 0)
+ return tryQueryInterface<TRest...>(riid, ppvObject);
+
+ *ppvObject = nullptr;
+
+ return E_NOINTERFACE;
+ }
+
+ std::atomic<LONG> m_referenceCount = 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q_OS_WIN
+
+#endif // QCOMOBJECT_P_H
diff --git a/src/corelib/platform/windows/qfactorycacheregistration_p.h b/src/corelib/platform/windows/qfactorycacheregistration_p.h
index 6a80ce63fa..d0b19b995b 100644
--- a/src/corelib/platform/windows/qfactorycacheregistration_p.h
+++ b/src/corelib/platform/windows/qfactorycacheregistration_p.h
@@ -23,7 +23,7 @@
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
-#include <winrt/base.h>
+#include "qt_winrtbase_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/platform/windows/qt_winrtbase_p.h b/src/corelib/platform/windows/qt_winrtbase_p.h
new file mode 100644
index 0000000000..fb7366f93d
--- /dev/null
+++ b/src/corelib/platform/windows/qt_winrtbase_p.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 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
+
+#ifndef QT_WINRTBASE_P_H
+#define QT_WINRTBASE_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 <QtCore/qglobal.h>
+
+#if QT_CONFIG(cpp_winrt)
+# include <winrt/base.h>
+# include <QtCore/private/qfactorycacheregistration_p.h>
+// Workaround for Windows SDK bug.
+// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
+namespace winrt::impl
+{
+ template <typename Async>
+ auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
+}
+// See https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/faq#how-do-i-resolve-ambiguities-with-getcurrenttime-and-or-try-
+// for more workarounds.
+#endif // QT_CONFIG(cpp/winrt)
+
+#endif // QT_WINRTBASE_P_H