5#include <private/qpermissions_p.h>
6#include <private/qstdweb_p.h>
8#include <qglobalstatic.h>
9#include <qpermissions.h>
10#include <qmetaobject.h>
11#include <qnamespace.h>
17#include <emscripten.h>
18#include <emscripten/bind.h>
19#include <emscripten/val.h>
27using namespace QPermissions::Private;
28using namespace emscripten;
32 constexpr const char *wapiGranted =
"granted";
33 constexpr const char *wapiDenied =
"denied";
34 constexpr const char *wapiPrompt =
"prompt";
35 constexpr const char *wapiCamera =
"camera";
36 constexpr const char *wapiVideoCapture =
"video_capture";
37 constexpr const char *wapiMicrophone =
"microphone";
38 constexpr const char *wapiAudioCapture =
"audio_capture";
39 constexpr const char *wapiGeolocation =
"geolocation";
41 void updatePermission(
const std::string &name,
const std::string &state,
42 PermissionCallback callback);
44 void checkPermission(
const std::string &permissionName)
46 val permissions = val::global(
"navigator")[
"permissions"];
47 if (permissions.isUndefined() || permissions.isNull())
50 qstdweb::PromiseCallbacks callbacks;
51 callbacks.thenFunc = [permissionName](val permissionState)
53 updatePermission(permissionName, permissionState[
"state"].as<
std::string>(), {});
55 callbacks.catchFunc = [permissionName](val err)
57 if (err[
"name"].as<std::string>() ==
"NotAllowedError"
58 || err[
"name"].as<std::string>() ==
"NotFoundError")
59 return updatePermission(permissionName, wapiDenied, {});
61 qCInfo(lcPermissions,
"'%s' '%s'", err[
"name"].as<
std::string>().c_str(),
62 err[
"message"].as<
std::string>().c_str());
65 val query = val::object();
66 query.set(
"name", val(permissionName));
68 qstdweb::Promise::make(permissions,
QStringLiteral(
"query"), callbacks, query);
71 void checkPermissions()
73 checkPermission(wapiCamera);
74 checkPermission(wapiMicrophone);
75 checkPermission(wapiGeolocation);
78 void bootstrapCheckPermissions()
80 QTimer::singleShot(0, []{checkPermissions();});
83 Q_CONSTRUCTOR_FUNCTION(bootstrapCheckPermissions);
85 int permissionTypeIdFromString(
const std::string &permission)
87 if (permission == wapiCamera || permission == wapiVideoCapture)
88 return qMetaTypeId<QCameraPermission>();
89 if (permission == wapiMicrophone || permission == wapiAudioCapture)
90 return qMetaTypeId<QMicrophonePermission>();
91 if (permission == wapiGeolocation)
92 return qMetaTypeId<QLocationPermission>();
94 qCWarning(lcPermissions,
"Unknown permission type '%s'", permission.c_str());
101 if (state == wapiGranted)
103 if (state == wapiDenied)
105 if (state == wapiPrompt)
108 qCWarning(lcPermissions,
"Unknown permission state '%s'", state.c_str());
116 void updatePermission(
const std::string &name,
const std::string &state, PermissionCallback callback)
118 qCDebug(lcPermissions) <<
"Updating" << name <<
"permission to" << state;
120 const int type = permissionTypeIdFromString(name);
124 const auto status = permissionStatusFromString(state);
125 (*permissionStatuses)[type] = status;
131 void requestMediaDevicePermission(
const std::string &device,
const PermissionCallback &cb)
135 val mediaDevices = val::global(
"navigator")[
"mediaDevices"];
136 if (mediaDevices.isUndefined())
139 qstdweb::PromiseCallbacks queryCallbacks;
140 queryCallbacks.thenFunc = [device, cb](val stream)
143 if (!stream.isNull() && !stream.isUndefined() && !stream[
"getTracks"].isUndefined()) {
144 emscripten::val tracks = stream.call<emscripten::val>(
"getTracks");
145 if (!tracks.isUndefined() && tracks[
"length"].as<
int>() > 0) {
146 for (
int i = 0; i < tracks[
"length"].as<
int>(); i++) {
147 tracks[i].call<
void>(
"stop");
151 stream = emscripten::val::null();
153 updatePermission(device, wapiGranted, cb);
155 queryCallbacks.catchFunc = [device, cb](val error)
157 qCInfo(lcPermissions) <<
"error"
158 << error[
"name"].as<
std::string>();
160 if (error[
"name"].as<std::string>() ==
"NotAllowedError")
161 return updatePermission(device, wapiDenied, cb);
162 updatePermission(device, wapiPrompt, cb);
165 val constraint = val::object();
166 if (device == wapiCamera)
167 constraint.set(
"video",
true);
169 constraint.set(
"audio",
true);
171 qstdweb::Promise::make(mediaDevices,
QStringLiteral(
"getUserMedia"), queryCallbacks, constraint);
174 using GeoRequest = std::pair<QPermission, PermissionCallback>;
177 bool processingLocationRequest =
false;
179 void processNextGeolocationRequest();
181 void geolocationSuccess(val position)
184 Q_ASSERT(geolocationRequestQueue->size());
186 processingLocationRequest =
false;
188 auto cb = geolocationRequestQueue->front().second;
189 geolocationRequestQueue->pop_front();
190 updatePermission(wapiGeolocation, wapiGranted, cb);
191 processNextGeolocationRequest();
194 void geolocationError(val error)
196 Q_ASSERT(geolocationRequestQueue->size());
198 static int deniedError = []
200 val posErr = val::global(
"GeolocationPositionError");
201 if (posErr.isUndefined() || posErr.isNull())
203 return posErr[
"PERMISSION_DENIED"].as<
int>();
206 processingLocationRequest =
false;
208 auto cb = geolocationRequestQueue->front().second;
209 geolocationRequestQueue->pop_front();
211 const int errorCode = error[
"code"].as<
int>();
212 updatePermission(wapiGeolocation, errorCode == deniedError ? wapiDenied : wapiPrompt, cb);
213 processNextGeolocationRequest();
216 EMSCRIPTEN_BINDINGS(qt_permissions) {
217 function(
"qtLocationSuccess", &geolocationSuccess);
218 function(
"qtLocationError", &geolocationError);
221 void processNextGeolocationRequest()
223 if (processingLocationRequest)
226 if (geolocationRequestQueue->empty())
229 processingLocationRequest =
true;
231 val geolocation = val::global(
"navigator")[
"geolocation"];
232 Q_ASSERT(!geolocation.isUndefined());
233 Q_ASSERT(!geolocation.isNull());
235 const auto &permission = geolocationRequestQueue->front().first;
236 const auto locationPermission = *permission.value<QLocationPermission>();
237 const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
239 val options = val::object();
240 options.set(
"enableHighAccuracy", highAccuracy ?
true :
false);
241 geolocation.call<
void>(
"getCurrentPosition", val::module_property(
"qtLocationSuccess"),
242 val::module_property(
"qtLocationError"), options);
245 void requestGeolocationPermission(
const QPermission &permission,
const PermissionCallback &cb)
248 Q_UNUSED(permission);
251 val geolocation = val::global(
"navigator")[
"geolocation"];
252 if (geolocation.isUndefined() || geolocation.isNull())
255 if (processingLocationRequest)
256 qCWarning(lcPermissions,
"Permission to access location requested, while another request is in progress");
258 geolocationRequestQueue->push_back(std::make_pair(permission, cb));
259 processNextGeolocationRequest();
267 const auto it = permissionStatuses->find(permission.type().id());
268 return it != permissionStatuses->end() ? it.value() : Qt::PermissionStatus::Undetermined;
273 Q_ASSERT(permission.type().isValid());
278 return callback(status);
280 const int requestedTypeId = permission.type().id();
281 if (requestedTypeId == qMetaTypeId<QCameraPermission>())
282 return requestMediaDevicePermission(wapiCamera, callback);
284 if (requestedTypeId == qMetaTypeId<QMicrophonePermission>())
285 return requestMediaDevicePermission(wapiMicrophone, callback);
287 if (requestedTypeId == qMetaTypeId<QLocationPermission>())
288 return requestGeolocationPermission(permission, callback);
290 (*permissionStatuses)[requestedTypeId] = Qt::PermissionStatus::Denied;
\inmodule QtCore \inheaderfile QPermissions
void requestPermission(const QPermission &permission, const PermissionCallback &callback)
Qt::PermissionStatus checkPermission(const QPermission &permission)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define QStringLiteral(str)