Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
24QT_BEGIN_NAMESPACE
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
49 qstdweb::PromiseCallbacks callbacks;
50 callbacks.thenFunc = [permissionName](val permissionState)
51 {
52 updatePermission(permissionName, permissionState["state"].as<std::string>(), {});
53 };
54 callbacks.catchFunc = [permissionName](val err)
55 {
56 if (err["name"].as<std::string>() == "NotAllowedError")
57 return updatePermission(permissionName, wapiDenied, {});
58
59 qCInfo(lcPermissions, "'%s' '%s'", err["name"].as<std::string>().c_str(),
60 err["message"].as<std::string>().c_str());
61 };
62
63 val query = val::object();
64 query.set("name", val(permissionName));
65
66 qstdweb::Promise::make(permissions, QStringLiteral("query"), callbacks, query);
67 }
68
69 void checkPermissions()
70 {
71 checkPermission(wapiCamera);
72 checkPermission(wapiMicrophone);
73 checkPermission(wapiGeolocation);
74 }
75
76 void bootstrapCheckPermissions()
77 {
78 QTimer::singleShot(0, []{checkPermissions();});
79 }
80
81 Q_CONSTRUCTOR_FUNCTION(bootstrapCheckPermissions);
82
83 int permissionTypeIdFromString(const std::string &permission)
84 {
85 if (permission == wapiCamera || permission == wapiVideoCapture)
86 return qMetaTypeId<QCameraPermission>();
87 if (permission == wapiMicrophone || permission == wapiAudioCapture)
88 return qMetaTypeId<QMicrophonePermission>();
89 if (permission == wapiGeolocation)
90 return qMetaTypeId<QLocationPermission>();
91
92 qCWarning(lcPermissions, "Unknown permission type '%s'", permission.c_str());
93
94 return -1;
95 }
96
97 Qt::PermissionStatus permissionStatusFromString(const std::string &state)
98 {
99 if (state == wapiGranted)
101 if (state == wapiDenied)
103 if (state == wapiPrompt)
105
106 qCWarning(lcPermissions, "Unknown permission state '%s'", state.c_str());
107
109 }
110
111 using PermissionHash = QHash<int, Qt::PermissionStatus>;
112 Q_GLOBAL_STATIC(PermissionHash, permissionStatuses);
113
114 void updatePermission(const std::string &name, const std::string &state, PermissionCallback callback)
115 {
116 qCDebug(lcPermissions) << "Updating" << name << "permission to" << state;
117
118 const int type = permissionTypeIdFromString(name);
119 if (type == -1)
120 return; // Unknown permission type
121
122 const auto status = permissionStatusFromString(state);
123 (*permissionStatuses)[type] = status;
124
125 if (callback)
126 callback(status);
127 }
128
129 void requestMediaDevicePermission(const std::string &device, const PermissionCallback &cb)
130 {
131 Q_ASSERT(cb);
132
133 val mediaDevices = val::global("navigator")["mediaDevices"];
134 if (mediaDevices.isUndefined())
136
137 qstdweb::PromiseCallbacks queryCallbacks;
138 queryCallbacks.thenFunc = [device, cb](val)
139 {
140 updatePermission(device, wapiGranted, cb);
141 };
142 queryCallbacks.catchFunc = [device, cb](val error)
143 {
144 if (error["name"].as<std::string>() == "NotAllowedError")
145 return updatePermission(device, wapiDenied, cb);
146 updatePermission(device, wapiPrompt, cb);
147 };
148
149 val constraint = val::object();
150 if (device == wapiCamera)
151 constraint.set("video", true);
152 else
153 constraint.set("audio", true);
154
155 qstdweb::Promise::make(mediaDevices, QStringLiteral("getUserMedia"), queryCallbacks, constraint);
156 }
157
158 using GeoRequest = std::pair<QPermission, PermissionCallback>;
159 Q_GLOBAL_STATIC(std::deque<GeoRequest>, geolocationRequestQueue);
160
161 bool processingLocationRequest = false;
162
163 void processNextGeolocationRequest();
164
165 void geolocationSuccess(val position)
166 {
167 Q_UNUSED(position);
168 Q_ASSERT(geolocationRequestQueue->size());
169
170 processingLocationRequest = false;
171
172 auto cb = geolocationRequestQueue->front().second;
173 geolocationRequestQueue->pop_front();
174 updatePermission(wapiGeolocation, wapiGranted, cb);
175 processNextGeolocationRequest();
176 }
177
178 void geolocationError(val error)
179 {
180 Q_ASSERT(geolocationRequestQueue->size());
181
182 static int deniedError = []
183 {
184 val posErr = val::global("GeolocationPositionError");
185 if (posErr.isUndefined() || posErr.isNull())
186 return 1;
187 return posErr["PERMISSION_DENIED"].as<int>();
188 }();
189
190 processingLocationRequest = false;
191
192 auto cb = geolocationRequestQueue->front().second;
193 geolocationRequestQueue->pop_front();
194
195 const int errorCode = error["code"].as<int>();
196 updatePermission(wapiGeolocation, errorCode == deniedError ? wapiDenied : wapiPrompt, cb);
197 processNextGeolocationRequest();
198 }
199
200 EMSCRIPTEN_BINDINGS(qt_permissions) {
201 function("qtLocationSuccess", &geolocationSuccess);
202 function("qtLocationError", &geolocationError);
203 }
204
205 void processNextGeolocationRequest()
206 {
207 if (processingLocationRequest)
208 return;
209
210 if (geolocationRequestQueue->empty())
211 return;
212
213 processingLocationRequest = true;
214
215 val geolocation = val::global("navigator")["geolocation"];
216 Q_ASSERT(!geolocation.isUndefined());
217 Q_ASSERT(!geolocation.isNull());
218
219 const auto &permission = geolocationRequestQueue->front().first;
220 const auto locationPermission = *permission.value<QLocationPermission>();
221 const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
222
223 val options = val::object();
224 options.set("enableHighAccuracy", highAccuracy ? true : false);
225 geolocation.call<void>("getCurrentPosition", val::module_property("qtLocationSuccess"),
226 val::module_property("qtLocationError"), options);
227 }
228
229 void requestGeolocationPermission(const QPermission &permission, const PermissionCallback &cb)
230 {
231 Q_ASSERT(cb);
232 Q_UNUSED(permission);
233 Q_UNUSED(cb);
234
235 val geolocation = val::global("navigator")["geolocation"];
236 if (geolocation.isUndefined() || geolocation.isNull())
238
239 if (processingLocationRequest)
240 qCWarning(lcPermissions, "Permission to access location requested, while another request is in progress");
241
242 geolocationRequestQueue->push_back(std::make_pair(permission, cb));
243 processNextGeolocationRequest();
244 }
245} // Unnamed namespace
246
247namespace QPermissions::Private
248{
250 {
251 const auto it = permissionStatuses->find(permission.type().id());
252 return it != permissionStatuses->end() ? it.value() : Qt::PermissionStatus::Undetermined;
253 }
254
255 void requestPermission(const QPermission &permission, const PermissionCallback &callback)
256 {
257 Q_ASSERT(permission.type().isValid());
258 Q_ASSERT(callback);
259
260 const auto status = checkPermission(permission);
262 return callback(status);
263
264 const int requestedTypeId = permission.type().id();
265 if (requestedTypeId == qMetaTypeId<QCameraPermission>())
266 return requestMediaDevicePermission(wapiCamera, callback);
267
268 if (requestedTypeId == qMetaTypeId<QMicrophonePermission>())
269 return requestMediaDevicePermission(wapiMicrophone, callback);
270
271 if (requestedTypeId == qMetaTypeId<QLocationPermission>())
272 return requestGeolocationPermission(permission, callback);
273
274 (*permissionStatuses)[requestedTypeId] = Qt::PermissionStatus::Denied;
276 }
277}
278
279QT_END_NAMESPACE
\inmodule QtCore
Definition qhash.h:837
\inmodule QtCore \inheaderfile QPermissions
void requestPermission(const QPermission &permission, const PermissionCallback &callback)
Qt::PermissionStatus checkPermission(const QPermission &permission)
Definition qcompare.h:76
PermissionStatus
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define QStringLiteral(str)
Definition qstring.h:1826