Skip to content

Commit

Permalink
WebXR should not work from an iFrame unless it is allowed to use the …
Browse files Browse the repository at this point in the history
…xr-spatial-tracking permissions policy

https://bugs.webkit.org/show_bug.cgi?id=271363
rdar://122963817

Reviewed by Mike Wyrzykowski.

Follow the latest WebXR spec and check whether 'xr-spatial-tracking' is enabled for
the requesting document's origin in WebGLRenderingContextBase::makeXRCompatible and
WebXRSystem::resolveRequestedFeatures.

Added layout tests to verify 'xr-spatial-tracking' permissions policy is checked
properly for xr.isSessionSupported, xr.requestSession, and WebGLRenderingContextBase::makeXRCompatible.
Set up symbolic links to the webxr related scripts from http/wpt/webxr folder so the
pages embedded in the iframes can reference them.

* LayoutTests/http/tests/webxr/resources/resources/test-only-api.js: Added.
* LayoutTests/http/tests/webxr/resources/resources/webxr_test_constants_single_view.js: Added.
* LayoutTests/http/tests/webxr/resources/resources/webxr_util.js: Added.
* LayoutTests/http/tests/webxr/resources/webxr-issessionsupported-test.html: Added.
* LayoutTests/http/tests/webxr/resources/webxr-makexrcompatible-test.html: Added.
* LayoutTests/http/tests/webxr/resources/webxr-requestsession-test.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-issessionsupported-allowed-by-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-issessionsupported-allowed-by-feature-policy.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-issessionsupported-denied-by-insufficient-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-issessionsupported-denied-by-insufficient-feature-policy.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-makexrcompatible-allowed-by-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-makexrcompatible-allowed-by-feature-policy.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-makexrcompatible-denied-by-insufficient-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-makexrcompatible-denied-by-insufficient-feature-policy.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-requestsession-allowed-by-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-requestsession-allowed-by-feature-policy.html: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-requestsession-denied-by-insufficient-feature-policy-expected.txt: Added.
* LayoutTests/http/tests/webxr/webxr-third-party-iframe-requestsession-denied-by-insufficient-feature-policy.html: Added.
* LayoutTests/platform/gtk/TestExpectations:
* LayoutTests/platform/ios/TestExpectations:
* LayoutTests/platform/mac-gpup/TestExpectations:
* LayoutTests/platform/mac/TestExpectations:
Skip http/tests/webxr tests in the same places where we've skipped other webxr layout tests.
* LayoutTests/platform/wpe/TestExpectations:
Filed a new bug on a crash found when calling WebXRSession destructor.
* LayoutTests/platform/wincairo/TestExpectations:
Skip http/tests/webxr tests in the same places where we've skipped other webxr layout tests.
* Source/WebCore/Modules/webxr/WebXRSystem.cpp:
(WebCore::WebXRSystem::isFeaturePermitted const):
(WebCore::WebXRSystem::resolveRequestedFeatures const):
* Source/WebCore/Modules/webxr/WebXRSystem.h:
* Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:
(WebCore::WebGLRenderingContextBase::makeXRCompatible):

Canonical link: https://commits.webkit.org/277301@main
  • Loading branch information
achan00 committed Apr 10, 2024
1 parent c2664b1 commit 7b007a8
Show file tree
Hide file tree
Showing 27 changed files with 294 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants_single_view.js"></script>
<script>
xr_promise_test(
"Tests isSessionSupported with immersive device connected",
async (t) => {
await navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE);
try {
const supported = await navigator.xr.isSessionSupported('immersive-vr');
top.postMessage({'isSessionSupported': supported}, '*');
} catch (e) {
top.postMessage({'isSessionSupported': e.name}, '*');
}
}
);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants_single_view.js"></script>
<script>
async function testMakeXRCompatible(t, gl) {
await navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE);
await gl.makeXRCompatible();
top.postMessage({'makeXRCompatible': gl.getContextAttributes().xrCompatible}, '*');
}

xr_promise_test(
"Tests makeXRCompatible with immersive device connected",
testMakeXRCompatible,
null,
'webgl'
);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants_single_view.js"></script>
<script>
xr_promise_test(
"Tests requestSession when connected to immersive device",
async (t) => {
await navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE);
navigator.xr.test.simulateUserActivation(async () => {
window.focus();
try {
const session = await navigator.xr.requestSession('immersive-vr');
top.postMessage({'requestSession': session != null}, '*');
} catch (e) {
top.postMessage({'requestSession': e.name}, '*');
}
});
}
);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Tests that third-party iframes will be allowed webxr session support with xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.isSessionSupported is true
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be allowed webxr session support with xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.isSessionSupported != undefined) {
shouldBeTrue("e.data.isSessionSupported");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-issessionsupported-test.html" allow="xr-spatial-tracking"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONSOLE MESSAGE: Feature policy 'XRSpatialTracking' check failed for element with origin 'http://localhost:8000' and allow attribute ''.
Tests that third-party iframes will be denied webxr session support without xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.isSessionSupported is "SecurityError"
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be denied webxr session support without xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.isSessionSupported != undefined) {
shouldBeEqualToString("e.data.isSessionSupported", "SecurityError");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-issessionsupported-test.html"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Tests that third-party iframes will be allowed to call makeXRCompatible() with xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.makeXRCompatible is true
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be allowed to call makeXRCompatible() with xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.makeXRCompatible != undefined) {
shouldBeTrue("e.data.makeXRCompatible");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-makexrcompatible-test.html" allow="xr-spatial-tracking"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONSOLE MESSAGE: Feature policy 'XRSpatialTracking' check failed for element with origin 'http://localhost:8000' and allow attribute ''.
Tests that third-party iframes will be denied from calling makeXRCompatible() without xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.makeXRCompatible is false
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be denied from calling makeXRCompatible() without xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.makeXRCompatible != undefined) {
shouldBeFalse("e.data.makeXRCompatible");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-makexrcompatible-test.html"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Tests that third-party iframes will be allowed to request webxr session with xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.requestSession is true
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be allowed to request webxr session with xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.requestSession != undefined) {
shouldBeTrue("e.data.requestSession");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-requestsession-test.html" allow="xr-spatial-tracking"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONSOLE MESSAGE: Feature policy 'XRSpatialTracking' check failed for element with origin 'http://localhost:8000' and allow attribute ''.
Tests that third-party iframes will be denied from requesting webxr session without xr-spatial-tracking feature policy.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS e.data.requestSession is "NotSupportedError"
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
description("Tests that third-party iframes will be denied from requesting webxr session without xr-spatial-tracking feature policy.");
jsTestIsAsync = true;

onmessage = (_e) => {
e = _e;
if (e.data.requestSession != undefined) {
shouldBeEqualToString("e.data.requestSession", "NotSupportedError");
finishJSTest();
}
};
</script>
<iframe src="http://localhost:8000/webxr/resources/webxr-requestSession-test.html"></iframe>
</body>
</html>
1 change: 1 addition & 0 deletions LayoutTests/platform/gtk/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ webkit.org/b/254733 inspector/canvas/shaderProgram-add-remove-webgl.html [ Failu
webkit.org/b/254733 inspector/canvas/shaderProgram-add-remove-webgl2.html [ Failure ]

# WebXR is not yet supported in GTK
webkit.org/b/208988 http/tests/webxr [ Skip ]
webkit.org/b/208988 http/wpt/webxr [ Skip ]
webkit.org/b/208988 imported/w3c/web-platform-tests/webxr [ Skip ]
webkit.org/b/208988 webxr [ Skip ]
Expand Down
1 change: 1 addition & 0 deletions LayoutTests/platform/ios/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -4463,6 +4463,7 @@ http/tests/misc/heic-accept-header.html [ Pass ]

webkit.org/b/254044 webxr [ Skip ]
webkit.org/b/254044 imported/w3c/web-platform-tests/webxr [ Skip ]
webkit.org/b/254044 http/tests/webxr [ Skip ]
webkit.org/b/254044 http/wpt/webxr [ Skip ]
webkit.org/b/254044 imported/w3c/web-platform-tests/feature-policy/reporting/xr-reporting.https.html [ Skip ]

Expand Down
1 change: 1 addition & 0 deletions LayoutTests/platform/mac-gpup/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ compositing/images/direct-image-object-fit.html [ Skip ]
compositing/reflections/direct-image-object-fit-reflected.html [ Skip ]

# rdar://102067140 WebXR tests crash
http/tests/webxr [ Skip ]
http/wpt/webxr [ Skip ]
imported/w3c/web-platform-tests/webxr [ Skip ]
webxr [ Skip ]
Expand Down
1 change: 1 addition & 0 deletions LayoutTests/platform/mac/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -2274,6 +2274,7 @@ http/tests/misc/heic-accept-header.html [ Pass ]

webkit.org/b/254044 webxr [ Skip ]
webkit.org/b/254044 imported/w3c/web-platform-tests/webxr [ Skip ]
webkit.org/b/254044 http/tests/webxr [ Skip ]
webkit.org/b/254044 http/wpt/webxr [ Skip ]
webkit.org/b/254044 imported/w3c/web-platform-tests/feature-policy/reporting/xr-reporting.https.html [ Skip ]

Expand Down
2 changes: 2 additions & 0 deletions LayoutTests/platform/wincairo/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@ http/tests/websocket/tests/hybi/send-object-tostring-check.html [ Pass Failure ]
http/tests/websocket/tests/hybi/websocket-blocked-sending-cookie-as-third-party-after-ws-redirect.html [ Failure ]
http/tests/websocket/tests/hybi/websocket-blocked-sending-cookie-as-third-party.html [ Failure ]

http/tests/webxr [ Skip ]

# Needs curl download support
http/tests/workers/service/service-worker-download-async-delegates.https.html [ Failure ]
http/tests/workers/service/service-worker-download.https.html [ Failure ]
Expand Down
2 changes: 2 additions & 0 deletions LayoutTests/platform/wpe/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -1758,3 +1758,5 @@ fast/text/whitespace/pre-wrap-015.html [ Timeout ]
webkit.org/b/272224 imported/w3c/web-platform-tests/selection/textcontrols/selectionchange.html [ Failure ]

webkit.org/b/272225 media/media-source/media-managedmse-resume-after-stall.html [ Failure ]

webkit.org/b/272426 http/tests/webxr/webxr-third-party-iframe-requestsession-denied-by-insufficient-feature-policy.html [ Crash Pass ]
22 changes: 22 additions & 0 deletions Source/WebCore/Modules/webxr/WebXRSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,26 @@ static bool featureRequiresExplicitConsent(PlatformXR::SessionFeature feature)
return false;
}

bool WebXRSystem::isFeaturePermitted(PlatformXR::SessionFeature feature) const
{
switch (feature) {
case PlatformXR::SessionFeature::ReferenceSpaceTypeViewer:
return true;
case PlatformXR::SessionFeature::ReferenceSpaceTypeLocal:
case PlatformXR::SessionFeature::ReferenceSpaceTypeLocalFloor:
case PlatformXR::SessionFeature::ReferenceSpaceTypeBoundedFloor:
case PlatformXR::SessionFeature::ReferenceSpaceTypeUnbounded:
#if ENABLE(WEBXR_HANDS)
case PlatformXR::SessionFeature::HandTracking:
#endif
RefPtr document = downcast<Document>(scriptExecutionContext());
return document && isPermissionsPolicyAllowedByDocumentAndAllOwners(PermissionsPolicy::Type::XRSpatialTracking, *document, LogPermissionsPolicyFailure::Yes);
}

ASSERT_NOT_REACHED();
return false;
}

bool WebXRSystem::isFeatureSupported(PlatformXR::SessionFeature feature, XRSessionMode mode, const PlatformXR::Device& device) const
{
if (!device.supportedFeatures(mode).contains(feature))
Expand Down Expand Up @@ -346,6 +366,8 @@ std::optional<WebXRSystem::ResolvedRequestedFeatures> WebXRSystem::resolveReques

// 4. If the requesting document's origin is not allowed to use any feature policy required by feature
// as indicated by the feature requirements table, (return null|continue to next entry).
if (!isFeaturePermitted(feature.value()))
RETURN_FALSE_OR_CONTINUE(returnOnFailure);

// 5. If session's XR device is not capable of supporting the functionality described by feature or the
// user agent has otherwise determined to reject the feature, (return null|continue to next entry).
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/Modules/webxr/WebXRSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class WebXRSystem final : public RefCounted<WebXRSystem>, public EventTarget, pu
bool immersiveSessionRequestIsAllowedForGlobalObject(LocalDOMWindow&, Document&) const;
bool inlineSessionRequestIsAllowedForGlobalObject(LocalDOMWindow&, Document&, const XRSessionInit&) const;

bool isFeaturePermitted(PlatformXR::SessionFeature) const;
bool isFeatureSupported(PlatformXR::SessionFeature, XRSessionMode, const PlatformXR::Device&) const;
struct ResolvedRequestedFeatures;
std::optional<ResolvedRequestedFeatures> resolveRequestedFeatures(XRSessionMode, const XRSessionInit&, RefPtr<PlatformXR::Device>, JSC::JSGlobalObject&) const;
Expand Down
14 changes: 11 additions & 3 deletions Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#include "OESTextureHalfFloatLinear.h"
#include "OESVertexArrayObject.h"
#include "Page.h"
#include "PermissionsPolicy.h"
#include "RenderBox.h"
#include "Settings.h"
#include "WebCodecsVideoFrame.h"
Expand Down Expand Up @@ -2854,9 +2855,16 @@ void WebGLRenderingContextBase::makeXRCompatible(MakeXRCompatiblePromise&& promi
return;
}

// 1. Let promise be a new Promise.
// 2. Let context be the target WebGLRenderingContextBase object.
// 3. Ensure an immersive XR device is selected.
// 1. If the requesting document’s origin is not allowed to use the "xr-spatial-tracking"
// permissions policy, resolve promise and return it.
if (!isPermissionsPolicyAllowedByDocumentAndAllOwners(PermissionsPolicy::Type::XRSpatialTracking, canvas->document(), LogPermissionsPolicyFailure::Yes)) {
promise.resolve();
return;
}

// 2. Let promise be a new Promise.
// 3. Let context be the target WebGLRenderingContextBase object.
// 4. Ensure an immersive XR device is selected.
auto& xrSystem = NavigatorWebXR::xr(window->navigator());
xrSystem.ensureImmersiveXRDeviceIsSelected([this, protectedThis = Ref { *this }, promise = WTFMove(promise), protectedXrSystem = Ref { xrSystem }]() mutable {
auto rejectPromiseWithInvalidStateError = makeScopeExit([&]() {
Expand Down

0 comments on commit 7b007a8

Please sign in to comment.