290 parseFeatures(init.optionalFeatures, ParsingMode::Loose);
291 return resolvedFeatures;
292}
293
294// https://immersive-web.github.io/webxr/#request-the-xr-permission
295bool WebXRSystem::isXRPermissionGranted(XRSessionMode mode, const XRSessionInit& init, PlatformXR::Device* device, JSC::JSGlobalObject& globalObject) const
296{
297 // 1. Set status's granted to an empty FrozenArray.
298 // 2. Let requiredFeatures be descriptor's requiredFeatures.
299 // 3. Let optionalFeatures be descriptor's optionalFeatures.
300 // 4. Let device be the result of obtaining the current device for mode, requiredFeatures, and optionalFeatures.
301
302 // 5. Let result be the result of resolving the requested features given requiredFeatures,optionalFeatures, and mode.
303 auto resolvedFeatures = resolveRequestedFeatures(mode, init, device, globalObject);
304
305 // 6. If result is null, run the following steps:
306 // 6.1. Set status's state to "denied".
307 // 6.2. Abort these steps.
308 if (!resolvedFeatures)
309 return false;
310
311 // 7. Let (consentRequired, consentOptional, granted) be the fields of result.
312 // 8. The user agent MAY at this point ask the user's permission for the calling algorithm to use any of the features
313 // in consentRequired and consentOptional. The results of these prompts should be included when determining if there
314 // is a clear signal of user intent for enabling these features.
315 // 9. For each feature in consentRequired perform the following steps:
316 // 9.1. The user agent MAY at this point ask the user's permission for the calling algorithm to use feature. The results
317 // of these prompts should be included when determining if there is a clear signal of user intent to enable feature.
318 // 9.2. If a clear signal of user intent to enable feature has not been determined, set status's state to "denied" and
319 // abort these steps.
320 // 9.3. If feature is not in granted, append feature to granted.
321 // 10. For each feature in consentOptional perform the following steps:
322 // 10.1. The user agent MAY at this point ask the user's permission for the calling algorithm to use feature. The results
323 // of these prompts should be included when determining if there is a clear signal of user intent to enable feature.
324 // 10.2. If a clear signal of user intent to enable feature has not been determined, continue to the next entry.
325 // 10.3. If feature is not in granted, append feature to granted.
326 // 11. Set status's granted to granted.
327 // 12. Set device's list of enabled features for mode to granted.
328 // 13. Set status's state to "granted".
329 return true;
330}
331
332
333// https://immersive-web.github.io/webxr/#dom-xrsystem-requestsession
334void WebXRSystem::requestSession(Document& document, XRSessionMode mode, const XRSessionInit& init, RequestSessionPromise&& promise)
335{
336 // 1. Let promise be a new Promise.
337 // 2. Let immersive be true if mode is an immersive session mode, and false otherwise.
338 // 3. Let global object be the relevant Global object for the XRSystem on which this method was invoked.
339 bool immersive = mode == XRSessionMode::ImmersiveAr || mode == XRSessionMode::ImmersiveVr;
340 auto* globalObject = document.domWindow();
341 if (!globalObject) {
342 promise.reject(Exception { InvalidAccessError});
343 return;
344 }
345
346 // 4. Check whether the session request is allowed as follows:
347 if (immersive) {
348 if (!immersiveSessionRequestIsAllowedForGlobalObject(*globalObject, document)) {
349 promise.reject(Exception { SecurityError });
350 return;
351 }
352 if (m_pendingImmersiveSession || m_activeImmersiveSession) {
353 promise.reject(Exception { InvalidStateError });
354 return;
355 }
356 m_pendingImmersiveSession = true;
357 } else {
358 if (!inlineSessionRequestIsAllowedForGlobalObject(*globalObject, document, init)) {
359 promise.reject(Exception { SecurityError });
360 return;
361 }
362 }
363
364 // 5. Run the following steps in parallel:
365 queueTaskKeepingObjectAlive(*this, TaskSource::WebXR, [this, document = makeRef(document), immersive, init, mode, promise = WTFMove(promise)] () mutable {
366 // 5.1 Let requiredFeatures be options' requiredFeatures.
367 // 5.2 Let optionalFeatures be options' optionalFeatures.
368 // 5.3 Set device to the result of obtaining the current device for mode, requiredFeatures, and optionalFeatures.
369 auto* device = obtainCurrentDevice(mode, init.requiredFeatures, init.optionalFeatures);
370
371 // 5.4 Queue a task to perform the following steps:
372 queueTaskKeepingObjectAlive(*this, TaskSource::WebXR, [this, &document, device = makeWeakPtr(device), immersive, init, mode, promise = WTFMove(promise)] () mutable {
373 auto rejectPromiseWithNotSupportedError = makeScopeExit([&] () {
374 promise.reject(Exception { NotSupportedError });
375 m_pendingImmersiveSession = false;
376 });
377
378 // 5.4.1 If device is null or device's list of supported modes does not contain mode, run the following steps:
379 // - Reject promise with a "NotSupportedError" DOMException.
380 // - If immersive is true, set pending immersive session to false.
381 // - Abort these steps.
382 if (!device || !device->supports(mode))
383 return;
384
385 auto* globalObject = document->execState();
386 if (!globalObject)
387 return;
388
389 // WebKit does not currently support the Permissions API. https://w3c.github.io/permissions/
390 // However we do implement here the permission request algorithm without the
391 // Permissions API bits as it handles, among others, the session features parsing. We also
392 // do it here before creating the session as there is no need to do it on advance.
393 // TODO: we just perform basic checks without asking any permission to the user so far. Maybe we should implement
394 // a mechanism similar to what others do involving passing a message to the UI process.
395
396 // 5.4.4 Let descriptor be an XRPermissionDescriptor initialized with session, requiredFeatures, and optionalFeatures
397 // 5.4.5 Let status be an XRPermissionStatus, initially null
398 // 5.4.6 Request the xr permission with descriptor and status.
399 // 5.4.7 If status' state is "denied" run the following steps: (same as above in 5.4.1)
400 if (!isXRPermissionGranted(mode, init, device.get(), *globalObject))
401 return;
402
403 // 5.4.2 Let session be a new XRSession object.
404 // 5.4.3 Initialize the session with session, mode, and device.
405 auto session = WebXRSession::create(document.get(), *this, mode, WTFMove(device));
406
407 // 5.4.8 Potentially set the active immersive session as follows:
408 if (immersive) {
409 m_activeImmersiveSession = session.copyRef();
410 m_pendingImmersiveSession = false;
411 } else
412 m_listOfInlineSessions.append(session.copyRef());
413
414 // 5.4.9 Resolve promise with session.
415 promise.resolve(session);
416 rejectPromiseWithNotSupportedError.release();
417
418 // TODO:
419 // 5.4.10 Queue a task to perform the following steps: NOTE: These steps ensure that initial inputsourceschange
420 // events occur after the initial session is resolved.
421 // 1. Set session's promise resolved flag to true.
422 // 2. Let sources be any existing input sources attached to session.
423 // 3. If sources is non-empty, perform the following steps:
424 // 1. Set session's list of active XR input sources to sources.
425 // 2. Fire an XRInputSourcesChangeEvent named inputsourceschange on session with added set to sources.
426
427 });
428 });