From 19ba3b03223167bbe1d33aceca01a3c1e1124b79 Mon Sep 17 00:00:00 2001 From: Kearwood Kip Gilbert Date: Thu, 12 Dec 2019 03:10:51 +0000 Subject: [PATCH] Bug 1580567 - Implement XR device access permission UI r=fluent-reviewers,bzbarsky,pbz,daoshengmu,imanol Added @rbarker as a reviewer to check if this will work well within GeckoView for FxR / Android. Added @bzbarsky for test_interfaces.html. -- I'd like to re-land the secure origin requirement for WebVR as part of this patch, as it doesn't help to have UI that can't guarantee the identity of the origin. (This was backed out due to test failures originally, and since been fixed) Differential Revision: https://phabricator.services.mozilla.com/D45951 --HG-- rename : browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js => browser/components/privatebrowsing/test/browser/browser_privatebrowsing_rememberprompt.js extra : moz-landing-system : lando --- browser/app/profile/firefox.js | 2 + browser/base/content/browser-siteIdentity.js | 36 +++++- browser/base/content/browser.xhtml | 5 + .../browser_temporary_permissions_expiry.js | 2 +- .../content/test/permissions/permissions.html | 1 + .../popupNotifications/browser_displayURI.js | 23 +++- browser/components/BrowserGlue.jsm | 3 + .../preferences/in-content/privacy.inc.xhtml | 24 ++++ .../preferences/in-content/privacy.js | 21 ++++ .../components/preferences/sitePermissions.js | 6 + .../privatebrowsing/test/browser/browser.ini | 5 +- ...browser_privatebrowsing_rememberprompt.js} | 68 +++++++----- ...browser_privatebrowsing_xrprompt_page.html | 11 ++ browser/locales/en-US/browser/browser.ftl | 4 + .../en-US/browser/preferences/permissions.ftl | 10 ++ .../en-US/browser/preferences/preferences.ftl | 5 + .../en-US/chrome/browser/browser.properties | 9 ++ .../chrome/browser/sitePermissions.properties | 1 + browser/modules/PermissionUI.jsm | 105 ++++++++++++++++++ browser/modules/SitePermissions.jsm | 4 + .../browser/browser_PermissionUI_prompts.js | 5 + .../test/browser/browser_SitePermissions.js | 12 ++ .../modules/test/unit/test_SitePermissions.js | 2 + .../themes/shared/incontentprefs/privacy.css | 4 + browser/themes/shared/jar.inc.mn | 2 + .../themes/shared/notification-icons.inc.css | 12 ++ .../shared/notification-icons/xr-blocked.svg | 10 ++ .../themes/shared/notification-icons/xr.svg | 9 ++ .../extension/lib/permissionPrompts.html | 1 + dom/base/Navigator.cpp | 71 +++++++----- dom/base/Navigator.h | 2 + dom/base/nsGlobalWindowInner.cpp | 42 ++++++- dom/base/nsGlobalWindowInner.h | 12 ++ .../mochitest/general/test_interfaces.js | 10 +- dom/vr/XRPermissionRequest.cpp | 74 ++++++++++++ dom/vr/XRPermissionRequest.h | 41 +++++++ dom/vr/moz.build | 6 +- dom/webidl/Navigator.webidl | 5 +- dom/webidl/VRDisplay.webidl | 13 ++- dom/webidl/VRDisplayEvent.webidl | 1 + .../permissions/PermissionDelegateHandler.cpp | 1 + testing/web-platform/meta/webvr/__dir__.ini | 4 +- 42 files changed, 603 insertions(+), 81 deletions(-) rename browser/components/privatebrowsing/test/browser/{browser_privatebrowsing_geoprompt.js => browser_privatebrowsing_rememberprompt.js} (54%) create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html create mode 100644 browser/themes/shared/notification-icons/xr-blocked.svg create mode 100644 browser/themes/shared/notification-icons/xr.svg create mode 100644 dom/vr/XRPermissionRequest.cpp create mode 100644 dom/vr/XRPermissionRequest.h diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index ab0162fe115f..0681cd241d6d 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -402,6 +402,7 @@ pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions"); pref("permissions.default.camera", 0); pref("permissions.default.microphone", 0); pref("permissions.default.geo", 0); +pref("permissions.default.xr", 0); pref("permissions.default.desktop-notification", 0); pref("permissions.default.shortcuts", 0); @@ -816,6 +817,7 @@ pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties"); pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/"); +pref("browser.xr.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/xr/"); pref("browser.sessionstore.resume_from_crash", true); pref("browser.sessionstore.resume_session_once", false); diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js index 2af729e85bd5..843fb5081fed 100644 --- a/browser/base/content/browser-siteIdentity.js +++ b/browser/base/content/browser-siteIdentity.js @@ -290,6 +290,11 @@ var gIdentityHandler = { return (this._geoSharingIcon = document.getElementById("geo-sharing-icon")); }, + get _xrSharingIcon() { + delete this._xrSharingIcon; + return (this._xrSharingIcon = document.getElementById("xr-sharing-icon")); + }, + get _webRTCSharingIcon() { delete this._webRTCSharingIcon; return (this._webRTCSharingIcon = document.getElementById( @@ -572,6 +577,7 @@ var gIdentityHandler = { this._webRTCSharingIcon.removeAttribute("paused"); this._webRTCSharingIcon.removeAttribute("sharing"); this._geoSharingIcon.removeAttribute("sharing"); + this._xrSharingIcon.removeAttribute("sharing"); if (this._sharingState) { if ( @@ -591,6 +597,9 @@ var gIdentityHandler = { if (this._sharingState.geo) { this._geoSharingIcon.setAttribute("sharing", this._sharingState.geo); } + if (this._sharingState.xr) { + this._xrSharingIcon.setAttribute("sharing", this._sharingState.xr); + } } if (this._identityPopup.state == "open") { @@ -1345,6 +1354,20 @@ var gIdentityHandler = { } } + if (this._sharingState && this._sharingState.xr) { + let xrPermission = permissions.find(perm => perm.id === "xr"); + if (xrPermission) { + xrPermission.sharingState = true; + } else { + permissions.push({ + id: "xr", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_REQUEST, + sharingState: true, + }); + } + } + if (this._sharingState && this._sharingState.webRTC) { let webrtcState = this._sharingState.webRTC; // If WebRTC device or screen permissions are in use, we need to find @@ -1572,9 +1595,12 @@ var gIdentityHandler = { return container; } - if (aPermission.id == "geo") { + if (aPermission.id == "geo" || aPermission.id == "xr") { let block = document.createXULElement("vbox"); - block.setAttribute("id", "identity-popup-geo-container"); + block.setAttribute( + "id", + "identity-popup-" + aPermission.id + "-container" + ); let button = this._createPermissionClearButton(aPermission, block); container.appendChild(button); @@ -1608,8 +1634,8 @@ var gIdentityHandler = { let browser = gBrowser.selectedBrowser; this._permissionList.removeChild(container); if (aPermission.sharingState) { - if (aPermission.id === "geo") { - let origins = browser.getDevicePermissionOrigins("geo"); + if (aPermission.id === "geo" || aPermission.id === "xr") { + let origins = browser.getDevicePermissionOrigins(aPermission.id); for (let origin of origins) { let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( origin @@ -1664,6 +1690,8 @@ var gIdentityHandler = { if (aPermission.id === "geo") { gBrowser.updateBrowserSharing(browser, { geo: false }); + } else if (aPermission.id === "xr") { + gBrowser.updateBrowserSharing(browser, { xr: false }); } }); diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml index 1d6eef568be2..c9cc8b00be26 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -888,10 +888,13 @@ + + + + diff --git a/browser/base/content/test/popupNotifications/browser_displayURI.js b/browser/base/content/test/popupNotifications/browser_displayURI.js index 2292b91eea25..7760a93b4395 100644 --- a/browser/base/content/test/popupNotifications/browser_displayURI.js +++ b/browser/base/content/test/popupNotifications/browser_displayURI.js @@ -30,7 +30,10 @@ async function check(contentTask, options = {}) { let panel = await popupShownPromise; let notification = panel.children[0]; let body = notification.querySelector(".popup-notification-body"); - if (notification.id == "geolocation-notification") { + if ( + notification.id == "geolocation-notification" || + notification.id == "xr-notification" + ) { ok( body.innerHTML.includes("local file"), `file:// URIs should be displayed as local file.` @@ -96,6 +99,12 @@ add_task(async function test_displayURI_geo() { }); }); +add_task(async function test_displayURI_xr() { + await check(async function() { + content.navigator.getVRDisplays(); + }); +}); + add_task(async function test_displayURI_camera() { await check(async function() { content.navigator.mediaDevices.getUserMedia({ video: true, fake: true }); @@ -115,6 +124,18 @@ add_task(async function test_displayURI_geo_blob() { ); }); +add_task(async function test_displayURI_xr_blob() { + await check( + async function() { + let text = ""; + let blob = new Blob([text], { type: "text/html" }); + let url = content.URL.createObjectURL(blob); + content.location.href = url; + }, + { skipOnExtension: true } + ); +}); + add_task(async function test_displayURI_camera_blob() { await check( async function() { diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index c7ed72402cff..8da7affa699a 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -4001,6 +4001,9 @@ const ContentPermissionIntegration = { case "geolocation": { return new PermissionUI.GeolocationPermissionPrompt(request); } + case "xr": { + return new PermissionUI.XRPermissionPrompt(request); + } case "desktop-notification": { return new PermissionUI.DesktopNotificationPermissionPrompt(request); } diff --git a/browser/components/preferences/in-content/privacy.inc.xhtml b/browser/components/preferences/in-content/privacy.inc.xhtml index 90537be5dcb0..1759f833a359 100644 --- a/browser/components/preferences/in-content/privacy.inc.xhtml +++ b/browser/components/preferences/in-content/privacy.inc.xhtml @@ -776,6 +776,30 @@ " /> + + + + + + + + + diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 8acdd5c27bfb..842ffbc5bf39 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1480,9 +1480,6 @@ already_AddRefed Navigator::GetVRDisplays(ErrorResult& aRv) { return nullptr; } - nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); - win->NotifyVREventListenerAdded(); - RefPtr p = Promise::Create(mWindow->AsGlobal(), aRv); if (aRv.Failed()) { return nullptr; @@ -1510,39 +1507,53 @@ already_AddRefed Navigator::GetVRDisplays(ErrorResult& aRv) { } void Navigator::FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p) { - if (isWebVRSupportedInwindow) { - nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); - - // Since FinishGetVRDisplays can be called asynchronously after an IPC - // response, it's possible that the Window can be torn down before this - // call. In that case, the Window's cyclic references to VR objects are - // also torn down and should not be recreated via - // NotifyVREventListenerAdded. - if (!win->IsDying()) { - win->NotifyVREventListenerAdded(); - // We pass mWindow's id to RefreshVRDisplays, so - // NotifyVRDisplaysUpdated will be called asynchronously, resolving - // the promises in mVRGetDisplaysPromises. - if (!VRDisplay::RefreshVRDisplays(win->WindowID())) { - // Failed to refresh, reject the promise now - p->MaybeRejectWithTypeError(u"Failed to find attached VR displays."); - } else { - // Succeeded, so cache the promise to resolve later - mVRGetDisplaysPromises.AppendElement(p); - } - } else { - // The Window has been torn down, so there is no further work that can - // be done. - p->MaybeRejectWithTypeError( - u"Unable to return VRDisplays for a closed window."); - } - } else { + if (!isWebVRSupportedInwindow) { // WebVR in this window is not supported, so resolve the promise // with no displays available nsTArray> vrDisplaysEmpty; p->MaybeResolve(vrDisplaysEmpty); + return; } + + // Since FinishGetVRDisplays can be called asynchronously after an IPC + // response, it's possible that the Window can be torn down before this + // call. In that case, the Window's cyclic references to VR objects are + // also torn down and should not be recreated via + // NotifyVREventListenerAdded. + nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); + if (win->IsDying()) { + // The Window has been torn down, so there is no further work that can + // be done. + p->MaybeRejectWithTypeError( + u"Unable to return VRDisplays for a closed window."); + return; + } + mVRGetDisplaysPromises.AppendElement(p); + win->RequestXRPermission(); +} + +void Navigator::OnXRPermissionRequestAllow() { + nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); + + // We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated will + // be called asynchronously, resolving the promises in mVRGetDisplaysPromises. + if (!VRDisplay::RefreshVRDisplays(win->WindowID())) { + for (auto& p : mVRGetDisplaysPromises) { + // Failed to refresh, reject the promise now + p->MaybeRejectWithTypeError(u"Failed to find attached VR displays."); + } + } +} + +void Navigator::OnXRPermissionRequestCancel() { + nsTArray> vrDisplays; + for (auto& p : mVRGetDisplaysPromises) { + // Resolve the promise with no vr displays when + // the user blocks access. + p->MaybeResolve(vrDisplays); + } + mVRGetDisplaysPromises.Clear(); } void Navigator::GetActiveVRDisplays( diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index fa22c7979fc7..1ad4f2a68017 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -181,6 +181,8 @@ class Navigator final : public nsISupports, public nsWrapperCache { already_AddRefed GetVRDisplays(ErrorResult& aRv); void FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p); void GetActiveVRDisplays(nsTArray>& aDisplays) const; + void OnXRPermissionRequestAllow(); + void OnXRPermissionRequestCancel(); VRServiceTest* RequestVRServiceTest(); bool IsWebVRContentDetected() const; bool IsWebVRContentPresenting() const; diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 7f56f79a856a..6ea1d8954c30 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -187,6 +187,7 @@ #include "mozilla/dom/VRDisplayEvent.h" #include "mozilla/dom/VRDisplayEventBinding.h" #include "mozilla/dom/VREventObserver.h" +#include "mozilla/dom/XRPermissionRequest.h" #include "nsRefreshDriver.h" #include "Layers.h" @@ -839,6 +840,8 @@ nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow, mHasGamepad(false), mHasVREvents(false), mHasVRDisplayActivateEvents(false), + mXRPermissionRequestInFlight(false), + mXRPermissionGranted(false), mHasSeenGamepadInput(false), mSuspendDepth(0), mFreezeDepth(0), @@ -1139,6 +1142,8 @@ void nsGlobalWindowInner::FreeInnerObjects() { DisableVRUpdates(); mHasVREvents = false; mHasVRDisplayActivateEvents = false; + mXRPermissionRequestInFlight = false; + mXRPermissionGranted = false; mVRDisplays.Clear(); // This breaks a cycle between the window and the ClientSource object. @@ -6014,13 +6019,48 @@ void nsGlobalWindowInner::SetHasGamepadEventListener( } } +void nsGlobalWindowInner::RequestXRPermission() { + if (mXRPermissionGranted) { + // Don't prompt redundantly once permission to + // access XR devices has been granted. + OnXRPermissionRequestAllow(); + return; + } + if (mXRPermissionRequestInFlight) { + // Don't allow multiple simultaneous permissions requests; + return; + } + mXRPermissionRequestInFlight = true; + RefPtr request = + new XRPermissionRequest(this, WindowID()); + Unused << NS_WARN_IF(NS_FAILED(request->Start())); +} + +void nsGlobalWindowInner::OnXRPermissionRequestAllow() { + mXRPermissionRequestInFlight = false; + mXRPermissionGranted = true; + + NotifyVREventListenerAdded(); + + dom::Navigator* nav = Navigator(); + MOZ_ASSERT(nav != nullptr); + nav->OnXRPermissionRequestAllow(); +} + +void nsGlobalWindowInner::OnXRPermissionRequestCancel() { + mXRPermissionRequestInFlight = false; + dom::Navigator* nav = Navigator(); + MOZ_ASSERT(nav != nullptr); + nav->OnXRPermissionRequestCancel(); +} + void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) { if (aType == nsGkAtoms::onvrdisplayactivate || aType == nsGkAtoms::onvrdisplayconnect || aType == nsGkAtoms::onvrdisplaydeactivate || aType == nsGkAtoms::onvrdisplaydisconnect || aType == nsGkAtoms::onvrdisplaypresentchange) { - NotifyVREventListenerAdded(); + RequestXRPermission(); } if (aType == nsGkAtoms::onvrdisplayactivate) { diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h index 37f0f805cf86..84bbbcf749d9 100644 --- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -371,6 +371,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, bool HasUsedVR() const; bool IsVRContentDetected() const; bool IsVRContentPresenting() const; + void RequestXRPermission(); + void OnXRPermissionRequestAllow(); + void OnXRPermissionRequestCancel(); using EventTarget::EventListenerAdded; virtual void EventListenerAdded(nsAtom* aType) override; @@ -1285,6 +1288,15 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, // Indicates whether this window wants VRDisplayActivate events bool mHasVRDisplayActivateEvents : 1; + + // Indicates that an XR permission request has been requested + // but has not yet been resolved. + bool mXRPermissionRequestInFlight : 1; + + // Indicates that an XR permission request has been granted. + // The page should not request permission multiple times. + bool mXRPermissionGranted : 1; + nsCheapSet mGamepadIndexSet; nsRefPtrHashtable mGamepads; bool mHasSeenGamepadInput; diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index a01552a9ddde..47ac9c083640 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -1296,43 +1296,37 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VisualViewport", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "VRDisplay", insecureContext: true, releaseNonWindowsAndMac: false }, + { name: "VRDisplay", releaseNonWindowsAndMac: false }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VRDisplayCapabilities", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VRDisplayEvent", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VREyeParameters", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VRFieldOfView", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VRFrameData", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "VRPose", insecureContext: true, releaseNonWindowsAndMac: false }, + { name: "VRPose", releaseNonWindowsAndMac: false }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "VRStageParameters", - insecureContext: true, releaseNonWindowsAndMac: false, }, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/vr/XRPermissionRequest.cpp b/dom/vr/XRPermissionRequest.cpp new file mode 100644 index 000000000000..87756c3fd056 --- /dev/null +++ b/dom/vr/XRPermissionRequest.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "XRPermissionRequest.h" +#include "nsIGlobalObject.h" +#include "mozilla/Preferences.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + +//------------------------------------------------- +// XR Permission Requests +//------------------------------------------------- + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XRPermissionRequest, + ContentPermissionRequestBase) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XRPermissionRequest, + ContentPermissionRequestBase) + +XRPermissionRequest::XRPermissionRequest(nsPIDOMWindowInner* aWindow, + uint64_t aWindowId) + : ContentPermissionRequestBase(aWindow->GetDoc()->NodePrincipal(), aWindow, + NS_LITERAL_CSTRING("dom.vr"), + NS_LITERAL_CSTRING("xr")), + mWindowId(aWindowId) { + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->GetDoc()); + mPrincipal = aWindow->GetDoc()->NodePrincipal(); + MOZ_ASSERT(mPrincipal); +} + +NS_IMETHODIMP +XRPermissionRequest::Cancel() { + nsGlobalWindowInner* window = + nsGlobalWindowInner::GetInnerWindowWithId(mWindowId); + if (!window) { + return NS_OK; + } + window->OnXRPermissionRequestCancel(); + return NS_OK; +} + +NS_IMETHODIMP +XRPermissionRequest::Allow(JS::HandleValue aChoices) { + MOZ_ASSERT(aChoices.isUndefined()); + nsGlobalWindowInner* window = + nsGlobalWindowInner::GetInnerWindowWithId(mWindowId); + if (!window) { + return NS_OK; + } + window->OnXRPermissionRequestAllow(); + return NS_OK; +} + +nsresult XRPermissionRequest::Start() { + MOZ_ASSERT(NS_IsMainThread()); + PromptResult pr = CheckPromptPrefs(); + if (pr == PromptResult::Granted) { + return Allow(JS::UndefinedHandleValue); + } + if (pr == PromptResult::Denied) { + return Cancel(); + } + + return nsContentPermissionUtils::AskPermission(this, mWindow); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/vr/XRPermissionRequest.h b/dom/vr/XRPermissionRequest.h new file mode 100644 index 000000000000..6ea2301bf28e --- /dev/null +++ b/dom/vr/XRPermissionRequest.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_XRPermissionRequest_h_ +#define mozilla_dom_XRPermissionRequest_h_ + +#include "mozilla/dom/Promise.h" +#include "nsContentPermissionHelper.h" +#include "nsISupports.h" + +namespace mozilla { +namespace dom { + +/** + * Handles permission dialog management when requesting XR device access. + */ +class XRPermissionRequest final : public ContentPermissionRequestBase { + public: + XRPermissionRequest(nsPIDOMWindowInner* aWindow, uint64_t aWindowId); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XRPermissionRequest, + ContentPermissionRequestBase) + // nsIContentPermissionRequest + NS_IMETHOD Cancel(void) override; + NS_IMETHOD Allow(JS::HandleValue choices) override; + nsresult Start(); + + private: + ~XRPermissionRequest() = default; + + uint64_t mWindowId; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_XR_h_ diff --git a/dom/vr/moz.build b/dom/vr/moz.build index b0b3af80f088..6b6f24c0cb3c 100644 --- a/dom/vr/moz.build +++ b/dom/vr/moz.build @@ -11,14 +11,16 @@ EXPORTS.mozilla.dom += [ 'VRDisplay.h', 'VRDisplayEvent.h', 'VREventObserver.h', - 'VRServiceTest.h' + 'VRServiceTest.h', + 'XRPermissionRequest.h', ] UNIFIED_SOURCES = [ 'VRDisplay.cpp', 'VRDisplayEvent.cpp', 'VREventObserver.cpp', - 'VRServiceTest.cpp' + 'VRServiceTest.cpp', + 'XRPermissionRequest.cpp', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index a6df66039e63..25f8bf1a14dc 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -213,11 +213,12 @@ partial interface Navigator { GamepadServiceTest requestGamepadServiceTest(); }; +// https://immersive-web.github.io/webvr/spec/1.1/#interface-navigator partial interface Navigator { - [Throws, Pref="dom.vr.enabled"] + [Throws, SecureContext, Pref="dom.vr.enabled"] Promise> getVRDisplays(); // TODO: Use FrozenArray once available. (Bug 1236777) - [Frozen, Cached, Pure, Pref="dom.vr.enabled"] + [SecureContext, Frozen, Cached, Pure, Pref="dom.vr.enabled"] readonly attribute sequence activeVRDisplays; [ChromeOnly, Pref="dom.vr.enabled"] readonly attribute boolean isWebVRContentDetected; diff --git a/dom/webidl/VRDisplay.webidl b/dom/webidl/VRDisplay.webidl index 104b62c67e87..52b1b66f9cbe 100644 --- a/dom/webidl/VRDisplay.webidl +++ b/dom/webidl/VRDisplay.webidl @@ -1,7 +1,11 @@ /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://immersive-web.github.io/webvr/spec/1.1/ + */ enum VREye { "left", @@ -10,6 +14,7 @@ enum VREye { [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRFieldOfView { readonly attribute double upDegrees; @@ -51,6 +56,7 @@ dictionary VRLayer { */ [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRDisplayCapabilities { /** @@ -93,6 +99,7 @@ interface VRDisplayCapabilities { */ [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRStageParameters { /** @@ -119,6 +126,7 @@ interface VRStageParameters { [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRPose { @@ -140,6 +148,7 @@ interface VRPose [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRFrameData { constructor(); @@ -157,6 +166,7 @@ interface VRFrameData { [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VREyeParameters { /** @@ -181,6 +191,7 @@ interface VREyeParameters { [Pref="dom.vr.enabled", HeaderFile="mozilla/dom/VRDisplay.h", + SecureContext, Exposed=Window] interface VRDisplay : EventTarget { /** diff --git a/dom/webidl/VRDisplayEvent.webidl b/dom/webidl/VRDisplayEvent.webidl index 320bda57c4a8..41407ec61507 100644 --- a/dom/webidl/VRDisplayEvent.webidl +++ b/dom/webidl/VRDisplayEvent.webidl @@ -16,6 +16,7 @@ dictionary VRDisplayEventInit : EventInit { }; [Pref="dom.vr.enabled", + SecureContext, Exposed=Window] interface VRDisplayEvent : Event { constructor(DOMString type, VRDisplayEventInit eventInitDict); diff --git a/extensions/permissions/PermissionDelegateHandler.cpp b/extensions/permissions/PermissionDelegateHandler.cpp index bded6d59f324..c726316f32d6 100644 --- a/extensions/permissions/PermissionDelegateHandler.cpp +++ b/extensions/permissions/PermissionDelegateHandler.cpp @@ -38,6 +38,7 @@ static const DelegateInfo sPermissionsMap[] = { {"camera", u"camera", DelegatePolicy::eDelegateUseFeaturePolicy}, {"microphone", u"microphone", DelegatePolicy::eDelegateUseFeaturePolicy}, {"screen", u"display-capture", DelegatePolicy::eDelegateUseFeaturePolicy}, + {"xr", nullptr, DelegatePolicy::ePersistDeniedCrossOrigin}, }; NS_IMPL_CYCLE_COLLECTION(PermissionDelegateHandler) diff --git a/testing/web-platform/meta/webvr/__dir__.ini b/testing/web-platform/meta/webvr/__dir__.ini index 46e5c53bd256..29acde9192c1 100644 --- a/testing/web-platform/meta/webvr/__dir__.ini +++ b/testing/web-platform/meta/webvr/__dir__.ini @@ -1 +1,3 @@ -prefs: [dom.vr.enabled:true, dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.experimental.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true] +prefs: [dom.vr.enabled:true, + dom.vr.prompt.testing:true, dom.vr.prompt.testing.allow:true, dom.security.featurePolicy.experimental.enabled:true, + dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]