Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qpermissions_wasm.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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 <private/qpermissions_p.h>
5#include <private/qstdweb_p.h>
6
7#include <qglobalstatic.h>
8#include <qpermissions.h>
9#include <qmetaobject.h>
10#include <qnamespace.h>
11#include <qmetatype.h>
12#include <qstring.h>
13#include <qtimer.h>
14#include <qhash.h>
15
16#include <emscripten.h>
17#include <emscripten/bind.h>
18#include <emscripten/val.h>
19
20#include <utility>
21#include <string>
22#include <queue>
23
25
26using namespace QPermissions::Private;
27using namespace emscripten;
28
29namespace
30{
31 constexpr const char *wapiGranted = "granted";
32 constexpr const char *wapiDenied = "denied";
33 constexpr const char *wapiPrompt = "prompt";
34 constexpr const char *wapiCamera = "camera";
35 constexpr const char *wapiVideoCapture = "video_capture"; // Alternative to "camera".
36 constexpr const char *wapiMicrophone = "microphone";
37 constexpr const char *wapiAudioCapture = "audio_capture"; // Alternative to "microphone".
38 constexpr const char *wapiGeolocation = "geolocation";
39
40 void updatePermission(const std::string &name, const std::string &state,
41 PermissionCallback callback);
42
43 void checkPermission(const std::string &permissionName)
44 {
45 val permissions = val::global("navigator")["permissions"];
46 if (permissions.isUndefined() || permissions.isNull())
47 return;
48
50 callbacks.thenFunc = [permissionName](val permissionState)
51 {
52 updatePermission(permissionName, permissionState["state"].as<std::string>(), {});
53 };
54 callbacks.catchFunc = [permissionName](val)
55 {
56 updatePermission(permissionName, wapiDenied, {});
57 };
58
59 val query = val::object();
60 query.set("name", val(permissionName));
61
63 }
64
65 void checkPermissions()
66 {
67 checkPermission(wapiCamera);
68 checkPermission(wapiMicrophone);
69 checkPermission(wapiGeolocation);
70 }
71
72 void bootstrapCheckPermissions()
73 {
74 QTimer::singleShot(0, []{checkPermissions();});
75 }
76
77 Q_CONSTRUCTOR_FUNCTION(bootstrapCheckPermissions);
78
79 int permissionTypeIdFromString(const std::string &permission)
80 {
81 if (permission == wapiCamera || permission == wapiVideoCapture)
82 return qMetaTypeId<QCameraPermission>();
83 if (permission == wapiMicrophone || permission == wapiAudioCapture)
84 return qMetaTypeId<QMicrophonePermission>();
85 if (permission == wapiGeolocation)
86 return qMetaTypeId<QLocationPermission>();
87
88 qCWarning(lcPermissions, "Unknown permission type '%s'", permission.c_str());
89
90 return -1;
91 }
92
93 Qt::PermissionStatus permissionStatusFromString(const std::string &state)
94 {
95 if (state == wapiGranted)
97 if (state == wapiDenied)
99 if (state == wapiPrompt)
101
102 qCWarning(lcPermissions, "Unknown permission state '%s'", state.c_str());
103
105 }
106
107 using PermissionHash = QHash<int, Qt::PermissionStatus>;
108 Q_GLOBAL_STATIC(PermissionHash, permissionStatuses);
109
110 void updatePermission(const std::string &name, const std::string &state, PermissionCallback callback)
111 {
112 qCDebug(lcPermissions) << "Updating" << name << "permission to" << state;
113
114 const int type = permissionTypeIdFromString(name);
115 if (type == -1)
116 return; // Unknown permission type
117
118 const auto status = permissionStatusFromString(state);
119 (*permissionStatuses)[type] = status;
120
121 if (callback)
122 callback(status);
123 }
124
125 void requestMediaDevicePermission(const std::string &device, const PermissionCallback &cb)
126 {
127 Q_ASSERT(cb);
128
129 val mediaDevices = val::global("navigator")["mediaDevices"];
130 if (mediaDevices.isUndefined())
132
133 qstdweb::PromiseCallbacks queryCallbacks;
134 queryCallbacks.thenFunc = [device, cb](val)
135 {
136 updatePermission(device, wapiGranted, cb);
137 };
138 queryCallbacks.catchFunc = [device, cb](val error)
139 {
140 if (error["name"].as<std::string>() == "NotAllowedError")
141 return updatePermission(device, wapiDenied, cb);
142 updatePermission(device, wapiPrompt, cb);
143 };
144
145 val constraint = val::object();
146 if (device == wapiCamera)
147 constraint.set("video", true);
148 else
149 constraint.set("audio", true);
150
151 qstdweb::Promise::make(mediaDevices, QStringLiteral("getUserMedia"), queryCallbacks, constraint);
152 }
153
154 using GeoRequest = std::pair<QPermission, PermissionCallback>;
155 Q_GLOBAL_STATIC(std::deque<GeoRequest>, geolocationRequestQueue);
156
157 bool processingLocationRequest = false;
158
159 void processNextGeolocationRequest();
160
161 void geolocationSuccess(val position)
162 {
164 Q_ASSERT(geolocationRequestQueue->size());
165
166 processingLocationRequest = false;
167
168 auto cb = geolocationRequestQueue->front().second;
169 geolocationRequestQueue->pop_front();
170 updatePermission(wapiGeolocation, wapiGranted, cb);
171 processNextGeolocationRequest();
172 }
173
174 void geolocationError(val error)
175 {
176 Q_ASSERT(geolocationRequestQueue->size());
177
178 static int deniedError = []
179 {
180 val posErr = val::global("GeolocationPositionError");
181 if (posErr.isUndefined() || posErr.isNull())
182 return 1;
183 return posErr["PERMISSION_DENIED"].as<int>();
184 }();
185
186 processingLocationRequest = false;
187
188 auto cb = geolocationRequestQueue->front().second;
189 geolocationRequestQueue->pop_front();
190
191 const int errorCode = error["code"].as<int>();
192 updatePermission(wapiGeolocation, errorCode == deniedError ? wapiDenied : wapiPrompt, cb);
193 processNextGeolocationRequest();
194 }
195
196 EMSCRIPTEN_BINDINGS(qt_permissions) {
197 function("qtLocationSuccess", &geolocationSuccess);
198 function("qtLocationError", &geolocationError);
199 }
200
201 void processNextGeolocationRequest()
202 {
203 if (processingLocationRequest)
204 return;
205
206 if (geolocationRequestQueue->empty())
207 return;
208
209 processingLocationRequest = true;
210
211 val geolocation = val::global("navigator")["geolocation"];
212 Q_ASSERT(!geolocation.isUndefined());
213 Q_ASSERT(!geolocation.isNull());
214
215 const auto &permission = geolocationRequestQueue->front().first;
216 const auto locationPermission = *permission.value<QLocationPermission>();
217 const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
218
219 val options = val::object();
220 options.set("enableHighAccuracy", highAccuracy ? true : false);
221 geolocation.call<void>("getCurrentPosition", val::module_property("qtLocationSuccess"),
222 val::module_property("qtLocationError"), options);
223 }
224
225 void requestGeolocationPermission(const QPermission &permission, const PermissionCallback &cb)
226 {
227 Q_ASSERT(cb);
228 Q_UNUSED(permission);
229 Q_UNUSED(cb);
230
231 val geolocation = val::global("navigator")["geolocation"];
232 if (geolocation.isUndefined() || geolocation.isNull())
234
235 if (processingLocationRequest)
236 qCWarning(lcPermissions, "Permission to access location requested, while another request is in progress");
237
238 geolocationRequestQueue->push_back(std::make_pair(permission, cb));
239 processNextGeolocationRequest();
240 }
241} // Unnamed namespace
242
243namespace QPermissions::Private
244{
246 {
247 const auto it = permissionStatuses->find(permission.type().id());
248 return it != permissionStatuses->end() ? it.value() : Qt::PermissionStatus::Undetermined;
249 }
250
251 void requestPermission(const QPermission &permission, const PermissionCallback &callback)
252 {
253 Q_ASSERT(permission.type().isValid());
254 Q_ASSERT(callback);
255
256 const auto status = checkPermission(permission);
258 return callback(status);
259
260 const int requestedTypeId = permission.type().id();
261 if (requestedTypeId == qMetaTypeId<QCameraPermission>())
262 return requestMediaDevicePermission(wapiCamera, callback);
263
264 if (requestedTypeId == qMetaTypeId<QMicrophonePermission>())
265 return requestMediaDevicePermission(wapiMicrophone, callback);
266
267 if (requestedTypeId == qMetaTypeId<QLocationPermission>())
268 return requestGeolocationPermission(permission, callback);
269
270 (*permissionStatuses)[requestedTypeId] = Qt::PermissionStatus::Denied;
272 }
273}
274
IOBluetoothDevice * device
Access the user's location.
Q_CORE_EXPORT Accuracy accuracy() const
Returns the accuracy of the request.
bool isValid() const
int id(int=0) const
Definition qmetatype.h:475
\inmodule QtCore \inheaderfile QPermissions
QMetaType type() const
Returns the type of the permission.
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
QSet< QString >::iterator it
else opt state
[0]
void requestPermission(const QPermission &permission, const PermissionCallback &callback)
std::function< void(Qt::PermissionStatus)> PermissionCallback
Qt::PermissionStatus checkPermission(const QPermission &permission)
Combined button and popup list for selecting options.
PermissionStatus
void make(emscripten::val target, QString methodName, PromiseCallbacks callbacks, Args... args)
Definition qstdweb_p.h:221
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum type
GLuint name
GLenum query
GLuint GLfloat * val
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
PromiseCallbacks callbacks
Definition qstdweb.cpp:275
#define QStringLiteral(str)
#define Q_UNUSED(x)
EMSCRIPTEN_BINDINGS(qtClipboardModule)
std::function< void(emscripten::val)> thenFunc
Definition qstdweb_p.h:212