Title: [292780] trunk
Revision
292780
Author
[email protected]
Date
2022-04-12 11:52:27 -0700 (Tue, 12 Apr 2022)

Log Message

[WebXR] Implement the WebXRFrame methods for getting joints' poses and radii
https://bugs.webkit.org/show_bug.cgi?id=238968

Patch by Ada Chan <[email protected]> on 2022-04-12
Reviewed by Dean Jackson.

Source/WebCore:

Tests: http/wpt/webxr/xrFrame_fillJointRadii.html
       http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html
       http/wpt/webxr/xrFrame_fillPoses.html
       http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html
       http/wpt/webxr/xrFrame_getJointPose.html
       http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html
       http/wpt/webxr/xrHand.html

- Implement the methods in WebXRFrame for getting joints' poses and radii.
- Add a new InputSourceHandJoint struct to represent joint data, and add
a vector of InputSourceHandJoint in PlatformXR::Device::FrameData::InputSource
to represent the joints data.
- Implement logic for managing a vector of WebXRJointSpaces in WebXRHand and
updating it with new hand joints data from PlatformXR::Device::FrameData::InputSource.

* DerivedSources-input.xcfilelist:
* DerivedSources-output.xcfilelist:
* DerivedSources.make:
* Modules/webxr/WebXRFrame+HandInput.idl:
* Modules/webxr/WebXRFrame.cpp:
(WebCore::WebXRFrame::populatePose):
There are valid cases when the WebXRSpace's effective origin can be null,
for example, a joint pose from a hand that has other missing joint poses.
Just return null and not throw an exception in that case.
(WebCore::WebXRFrame::getJointPose):
Populate the joint's pose, and create a WebXRJointPose with the pose's
transform and the joint's radius.
(WebCore::WebXRFrame::fillJointRadii):
Validate the inputs and fill in the radii array. Fill in with NaN
if the hand has missing joint poses.
(WebCore::WebXRFrame::fillPoses):
Validate the inputs and fill in the transforms array. Fill in with NaN
if the hand has missing joint poses.
* Modules/webxr/WebXRFrame.h:
* Modules/webxr/WebXRHand.cpp:
(WebCore::WebXRHand::WebXRHand):
Initialize the m_joints array.
(WebCore::WebXRHand::get):
(WebCore::WebXRHand::Iterator::next):
(WebCore::WebXRHand::session):
(WebCore::WebXRHand::updateFromInputSource):
Update m_joints array based on the latest hands data from InputSource.
* Modules/webxr/WebXRHand.h:
(WebCore::WebXRHand::size):
(WebCore::WebXRHand::hasMissingPoses const):
* Modules/webxr/WebXRInputSource.cpp:
(WebCore::WebXRInputSource::update):
* Modules/webxr/WebXRJointPose.cpp:
(WebCore::WebXRJointPose::create):
(WebCore::WebXRJointPose::WebXRJointPose):
Handle joint radius.
* Modules/webxr/WebXRJointPose.h:
* Modules/webxr/WebXRJointPose.idl:
* Modules/webxr/WebXRJointSpace.cpp:
(WebCore::WebXRJointSpace::create):
(WebCore::WebXRJointSpace::WebXRJointSpace):
Handle joint name and the actual pose data. Keep a weak ptr to the WebXRHand.
(WebCore::WebXRJointSpace::updateFromJoint):
(WebCore::WebXRJointSpace::handHasMissingPoses const):
(WebCore::WebXRJointSpace::session const):
(WebCore::WebXRJointSpace::nativeOrigin const):
Return null pose transforms if there are missing joint poses.
* Modules/webxr/WebXRJointSpace.h:
(WebCore::WebXRJointSpace::jointName const):
(WebCore::WebXRJointSpace::radius const):
* Modules/webxr/WebXRJointSpace.idl:
* Modules/webxr/WebXRSession.cpp:
(WebCore::WebXRSession::isHandTrackingEnabled const):
* Modules/webxr/WebXRSession.h:
* WebCore.xcodeproj/project.pbxproj:
* platform/xr/PlatformXR.h:
(PlatformXR::Device::FrameData::InputSourceHandJoint::encode const):
(PlatformXR::Device::FrameData::InputSourceHandJoint::decode):
(PlatformXR::Device::FrameData::InputSource::encode const):
(PlatformXR::Device::FrameData::InputSource::decode):
* testing/FakeXRInputSourceInit.h:
* testing/FakeXRInputSourceInit.idl:
Allow test InputSource to be created with an array of joints.
* testing/FakeXRJointStateInit.h: Copied from Source/WebCore/Modules/webxr/WebXRJointPose.idl.
* testing/FakeXRJointStateInit.idl: Copied from Source/WebCore/Modules/webxr/WebXRJointPose.idl.
Define init dictionary for joint data.
* testing/WebFakeXRInputController.cpp:
(WebCore::WebFakeXRInputController::WebFakeXRInputController):
(WebCore::WebFakeXRInputController::getFrameData):
(WebCore::WebFakeXRInputController::updateHandJoints):
Update m_handJoints based on joint init dictionaries.
* testing/WebFakeXRInputController.h:
* testing/WebFakeXRInputController.idl:
* testing/WebXRTest.cpp:
(WebCore::parseFeatures):
(WebCore::WebXRTest::simulateDeviceConnection):
Allow test device to specify a list of enabled features that don't need
further explicit consent.
* testing/WebXRTest.h:
* testing/WebXRTest.idl:

Source/WebKit:

* Shared/XR/XRDeviceProxy.cpp:
(WebKit::XRDeviceProxy::initializeTrackingAndRendering):
Initialize input sources on session start.

LayoutTests:

Add more constants for a valid joint value and device init dictionary
that represents a device with hand tracking support enabled.

Add new tests for testing hand input related methods in XRFrame.

* http/wpt/webxr/resources/webxr_test_constants_single_view.js:
* http/wpt/webxr/xrFrame_fillJointRadii-expected.txt: Added.
* http/wpt/webxr/xrFrame_fillJointRadii.html: Added.
* http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose-expected.txt: Added.
* http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html: Added.
* http/wpt/webxr/xrFrame_fillPoses-expected.txt: Added.
* http/wpt/webxr/xrFrame_fillPoses.html: Added.
* http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose-expected.txt: Added.
* http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html: Added.
* http/wpt/webxr/xrFrame_getJointPose-expected.txt: Added.
* http/wpt/webxr/xrFrame_getJointPose.html: Added.
* http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose-expected.txt: Added.
* http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html: Added.
* http/wpt/webxr/xrHand-expected.txt: Added.
* http/wpt/webxr/xrHand.html: Added.
* http/wpt/webxr/xrHandInput_gc.html:
* platform/win/TestExpectations: Skip other folders with WebXR tests

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (292779 => 292780)


--- trunk/LayoutTests/ChangeLog	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/LayoutTests/ChangeLog	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1,3 +1,33 @@
+2022-04-12  Ada Chan  <[email protected]>
+
+        [WebXR] Implement the WebXRFrame methods for getting joints' poses and radii
+        https://bugs.webkit.org/show_bug.cgi?id=238968
+
+        Reviewed by Dean Jackson.
+
+        Add more constants for a valid joint value and device init dictionary
+        that represents a device with hand tracking support enabled.
+
+        Add new tests for testing hand input related methods in XRFrame.
+
+        * http/wpt/webxr/resources/webxr_test_constants_single_view.js:
+        * http/wpt/webxr/xrFrame_fillJointRadii-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_fillJointRadii.html: Added.
+        * http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html: Added.
+        * http/wpt/webxr/xrFrame_fillPoses-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_fillPoses.html: Added.
+        * http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html: Added.
+        * http/wpt/webxr/xrFrame_getJointPose-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_getJointPose.html: Added.
+        * http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose-expected.txt: Added.
+        * http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html: Added.
+        * http/wpt/webxr/xrHand-expected.txt: Added.
+        * http/wpt/webxr/xrHand.html: Added.
+        * http/wpt/webxr/xrHandInput_gc.html:
+        * platform/win/TestExpectations: Skip other folders with WebXR tests
+
 2022-04-12  Karl Rackler  <[email protected]>
 
         REGRESSION (r292043): [ Mac ] fast/block/positioning/fixed-container-with-relative-parent.html is a flaky image failure

Modified: trunk/LayoutTests/http/wpt/webxr/resources/webxr_test_constants_single_view.js (292779 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/resources/webxr_test_constants_single_view.js	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/LayoutTests/http/wpt/webxr/resources/webxr_test_constants_single_view.js	2022-04-12 18:52:27 UTC (rev 292780)
@@ -47,6 +47,74 @@
     orientation: [0, 0, 0, 1]
 };
 
+const JOINT_COUNT = 25;
+const TEST_HAND_JOINT_RADIUS = 0.02;
+
+const VALID_HAND_JOINT = {
+    pose: VALID_POSE_TRANSFORM,
+    radius: TEST_HAND_JOINT_RADIUS
+};
+
+const VALID_HAND_JOINTS = [
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT
+];
+
+const INVALID_HAND_JOINT = {
+
+};
+
+const HAND_JOINTS_WITH_ONE_INVALID = [
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    VALID_HAND_JOINT,
+    INVALID_HAND_JOINT
+];
+
 // A valid input pointer offset for  when we don't care about specific values
 const VALID_POINTER = [1, 0, 0, 0,
                        0, 1, 0, 0,
@@ -160,6 +228,17 @@
     interactionMode: "world-space"
 };
 
+const TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING = {
+    supportsImmersive: true,
+    supportedModes: [ "inline", "immersive-vr"],
+    views: VALID_VIEWS,
+    viewerOrigin: IDENTITY_TRANSFORM,
+    supportedFeatures: ALL_FEATURES,
+    enabledFeatures: [ "hand-tracking" ],
+    environmentBlendMode: "opaque",
+    interactionMode: "world-space"
+};
+
 const TRACKED_IMMERSIVE_DEVICE_NO_HAND_TRACKING = {
     supportsImmersive: true,
     supportedModes: [ "inline", "immersive-vr"],

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.fillJointRadii - webgl
+PASS Test XRFrame.fillJointRadii - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.fillJointRadii";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: VALID_HAND_JOINTS,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+
+        assert_throws_js(TypeError, function() {
+            let radiiArrayWithWrongSize = new Float32Array(JOINT_COUNT - 1);
+            xrFrame.fillJointRadii(hand.values(), radiiArrayWithWrongSize);
+        });
+
+        let radii = new Float32Array(JOINT_COUNT);
+        let allValid = xrFrame.fillJointRadii(hand.values(), radii);
+        assert_true(allValid);
+        for (radius of radii) {
+            assert_approx_equals(radius, TEST_HAND_JOINT_RADIUS, FLOAT_EPSILON);
+        }
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.fillJointRadii where the hand has one missing joint pose - webgl
+PASS Test XRFrame.fillJointRadii where the hand has one missing joint pose - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.fillJointRadii where the hand has one missing joint pose";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: HAND_JOINTS_WITH_ONE_INVALID,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+        assert_equals(hand.size, JOINT_COUNT);
+
+        let radii = new Float32Array(JOINT_COUNT);
+        let allValid = xrFrame.fillJointRadii(hand.values(), radii);
+        assert_false(allValid);
+        for (radius of radii) {
+            assert_equals(radius, NaN);
+        }
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.fillPoses - webgl
+PASS Test XRFrame.fillPoses - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.fillPoses";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: VALID_HAND_JOINTS,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+
+        const floatsPerTransform = 16;
+        assert_throws_js(TypeError, function() {
+            let transformsArrayWithWrongSize = new Float32Array(JOINT_COUNT * floatsPerTransform - 1);
+            xrFrame.fillPoses(hand.values(), transformsArrayWithWrongSize);
+        });
+
+        let transforms = new Float32Array(JOINT_COUNT * floatsPerTransform);
+        let allValid = xrFrame.fillPoses(hand.values(), referenceSpace, transforms);
+        assert_true(allValid);
+        
+        for (var i = 0; i < JOINT_COUNT; ++i) {
+            let transformStartIndex = i * floatsPerTransform;
+            for (var j = 0; j < floatsPerTransform; ++j) {
+                assert_approx_equals(transforms[transformStartIndex + j], VALID_POSE_MATRIX[j], FLOAT_EPSILON);
+            }
+        }
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.fillPoses where the hand has one missing joint pose - webgl
+PASS Test XRFrame.fillPoses where the hand has one missing joint pose - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.fillPoses where the hand has one missing joint pose";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: HAND_JOINTS_WITH_ONE_INVALID,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+        assert_equals(hand.size, JOINT_COUNT);
+
+        const floatsPerTransform = 16;
+        let transforms = new Float32Array(JOINT_COUNT * floatsPerTransform);
+        let allValid = xrFrame.fillPoses(hand.values(), referenceSpace, transforms);
+        assert_false(allValid);
+        
+        for (var i = 0; i < JOINT_COUNT * floatsPerTransform; ++i) {
+            assert_equals(transforms[i], NaN);
+        }
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.getJointPose - webgl
+PASS Test XRFrame.getJointPose - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.getJointPose";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: VALID_HAND_JOINTS,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+        
+        let jointSpace = hand.get("wrist");
+        assert_not_equals(jointSpace, null);
+        
+        let jointPose = xrFrame.getJointPose(jointSpace, referenceSpace);
+        assert_not_equals(jointPose, null);
+        assert_approx_equals(jointPose.radius, TEST_HAND_JOINT_RADIUS, FLOAT_EPSILON);
+        assert_matrix_approx_equals(jointPose.transform.matrix, VALID_POSE_MATRIX, FLOAT_EPSILON);
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test XRFrame.getJointPose where the hand has one missing joint pose - webgl
+PASS Test XRFrame.getJointPose where the hand has one missing joint pose - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test XRFrame.getJointPose where the hand has one missing joint pose";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: HAND_JOINTS_WITH_ONE_INVALID,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+        assert_equals(hand.size, JOINT_COUNT);
+
+        let wristJointSpace = hand.get("wrist");
+        assert_not_equals(wristJointSpace, null);
+        assert_equals(xrFrame.getJointPose(wristJointSpace, referenceSpace), null);
+
+        let pinkyTipJointSpace = hand.get("pinky-finger-tip");
+        assert_not_equals(pinkyTipJointSpace, null);
+        assert_equals(xrFrame.getJointPose(pinkyTipJointSpace, referenceSpace), null);
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Added: trunk/LayoutTests/http/wpt/webxr/xrHand-expected.txt (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrHand-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrHand-expected.txt	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,4 @@
+
+PASS Test the XRHand API - webgl
+PASS Test the XRHand API - webgl2
+

Added: trunk/LayoutTests/http/wpt/webxr/xrHand.html (0 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrHand.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/webxr/xrHand.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+
+<script>
+let testName = "Test the XRHand API";
+
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
+
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+
+  let input_source = fakeDeviceController.simulateInputSourceConnection({
+    handedness: "none",
+    targetRayMode: "tracked-pointer",
+    pointerOrigin: VALID_POINTER_TRANSFORM,
+    profiles: [],
+    handJoints: VALID_HAND_JOINTS,
+  });
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => new Promise((resolve) => {
+
+      function onFrame(time, xrFrame) {
+        assert_not_equals(session.inputSources.length, 0);
+        let hand = session.inputSources[0].hand
+        assert_not_equals(hand, null);
+        assert_equals(hand.size, 25);
+        
+        let wristJointSpace = hand.get("wrist");
+        assert_equals(wristJointSpace.jointName, "wrist");
+
+        var count = 0;
+        for (const [jointName, jointSpace] of hand) {
+            assert_equals(jointSpace.jointName, jointName);
+            count++;
+        }
+        assert_equals(count, 25);
+
+        resolve();
+      }
+      session.requestAnimationFrame(onFrame);
+  }));
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { optionalFeatures: ["hand-tracking"] });
+
+</script>

Modified: trunk/LayoutTests/http/wpt/webxr/xrHandInput_gc.html (292779 => 292780)


--- trunk/LayoutTests/http/wpt/webxr/xrHandInput_gc.html	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/LayoutTests/http/wpt/webxr/xrHandInput_gc.html	2022-04-12 18:52:27 UTC (rev 292780)
@@ -17,7 +17,7 @@
 
 let testName = "Test object lifetime of Hand Input module";
 
-let fakeDeviceInitParams = TRACKED_IMMERSIVE_SINGLE_VIEWDEVICE;
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE_WITH_HAND_TRACKING;
 
 let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
 
@@ -26,7 +26,7 @@
     targetRayMode: "tracked-pointer",
     pointerOrigin: VALID_POINTER_TRANSFORM,
     profiles: [],
-    simulateHand: true,
+    handJoints: VALID_HAND_JOINTS,
   });
 
   return session.requestReferenceSpace('local')

Modified: trunk/LayoutTests/platform/win/TestExpectations (292779 => 292780)


--- trunk/LayoutTests/platform/win/TestExpectations	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/LayoutTests/platform/win/TestExpectations	2022-04-12 18:52:27 UTC (rev 292780)
@@ -4910,6 +4910,8 @@
 
 # WebXR is not enabled on windows.
 webxr [ Skip ]
+imported/w3c/web-platform-tests/webxr [ Skip ]
+http/wpt/webxr [ Skip ]
 
 # https://bugs.webkit.org/show_bug.cgi?id=226970
 fast/css/parse-border-image-repeat-null-crash.html [ Failure ]

Modified: trunk/Source/WebCore/ChangeLog (292779 => 292780)


--- trunk/Source/WebCore/ChangeLog	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/ChangeLog	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1,3 +1,106 @@
+2022-04-12  Ada Chan  <[email protected]>
+
+        [WebXR] Implement the WebXRFrame methods for getting joints' poses and radii
+        https://bugs.webkit.org/show_bug.cgi?id=238968
+
+        Reviewed by Dean Jackson.
+
+        Tests: http/wpt/webxr/xrFrame_fillJointRadii.html
+               http/wpt/webxr/xrFrame_fillJointRadii_missing_joint_pose.html
+               http/wpt/webxr/xrFrame_fillPoses.html
+               http/wpt/webxr/xrFrame_fillPoses_missing_joint_pose.html
+               http/wpt/webxr/xrFrame_getJointPose.html
+               http/wpt/webxr/xrFrame_getJointPose_missing_joint_pose.html
+               http/wpt/webxr/xrHand.html
+
+        - Implement the methods in WebXRFrame for getting joints' poses and radii.
+        - Add a new InputSourceHandJoint struct to represent joint data, and add
+        a vector of InputSourceHandJoint in PlatformXR::Device::FrameData::InputSource
+        to represent the joints data.
+        - Implement logic for managing a vector of WebXRJointSpaces in WebXRHand and
+        updating it with new hand joints data from PlatformXR::Device::FrameData::InputSource.
+
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources-output.xcfilelist:
+        * DerivedSources.make:
+        * Modules/webxr/WebXRFrame+HandInput.idl:
+        * Modules/webxr/WebXRFrame.cpp:
+        (WebCore::WebXRFrame::populatePose):
+        There are valid cases when the WebXRSpace's effective origin can be null,
+        for example, a joint pose from a hand that has other missing joint poses.
+        Just return null and not throw an exception in that case. 
+        (WebCore::WebXRFrame::getJointPose):
+        Populate the joint's pose, and create a WebXRJointPose with the pose's 
+        transform and the joint's radius.
+        (WebCore::WebXRFrame::fillJointRadii):
+        Validate the inputs and fill in the radii array. Fill in with NaN
+        if the hand has missing joint poses.
+        (WebCore::WebXRFrame::fillPoses):
+        Validate the inputs and fill in the transforms array. Fill in with NaN
+        if the hand has missing joint poses.
+        * Modules/webxr/WebXRFrame.h:
+        * Modules/webxr/WebXRHand.cpp:
+        (WebCore::WebXRHand::WebXRHand):
+        Initialize the m_joints array.
+        (WebCore::WebXRHand::get):
+        (WebCore::WebXRHand::Iterator::next):
+        (WebCore::WebXRHand::session):
+        (WebCore::WebXRHand::updateFromInputSource):
+        Update m_joints array based on the latest hands data from InputSource.
+        * Modules/webxr/WebXRHand.h:
+        (WebCore::WebXRHand::size):
+        (WebCore::WebXRHand::hasMissingPoses const):
+        * Modules/webxr/WebXRInputSource.cpp:
+        (WebCore::WebXRInputSource::update):
+        * Modules/webxr/WebXRJointPose.cpp:
+        (WebCore::WebXRJointPose::create):
+        (WebCore::WebXRJointPose::WebXRJointPose):
+        Handle joint radius.
+        * Modules/webxr/WebXRJointPose.h:
+        * Modules/webxr/WebXRJointPose.idl:
+        * Modules/webxr/WebXRJointSpace.cpp:
+        (WebCore::WebXRJointSpace::create):
+        (WebCore::WebXRJointSpace::WebXRJointSpace):
+        Handle joint name and the actual pose data. Keep a weak ptr to the WebXRHand.
+        (WebCore::WebXRJointSpace::updateFromJoint):
+        (WebCore::WebXRJointSpace::handHasMissingPoses const):
+        (WebCore::WebXRJointSpace::session const):
+        (WebCore::WebXRJointSpace::nativeOrigin const):
+        Return null pose transforms if there are missing joint poses.
+        * Modules/webxr/WebXRJointSpace.h:
+        (WebCore::WebXRJointSpace::jointName const):
+        (WebCore::WebXRJointSpace::radius const):
+        * Modules/webxr/WebXRJointSpace.idl:
+        * Modules/webxr/WebXRSession.cpp:
+        (WebCore::WebXRSession::isHandTrackingEnabled const):
+        * Modules/webxr/WebXRSession.h:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/xr/PlatformXR.h:
+        (PlatformXR::Device::FrameData::InputSourceHandJoint::encode const):
+        (PlatformXR::Device::FrameData::InputSourceHandJoint::decode):
+        (PlatformXR::Device::FrameData::InputSource::encode const):
+        (PlatformXR::Device::FrameData::InputSource::decode):
+        * testing/FakeXRInputSourceInit.h:
+        * testing/FakeXRInputSourceInit.idl:
+        Allow test InputSource to be created with an array of joints.
+        * testing/FakeXRJointStateInit.h: Copied from Source/WebCore/Modules/webxr/WebXRJointPose.idl.
+        * testing/FakeXRJointStateInit.idl: Copied from Source/WebCore/Modules/webxr/WebXRJointPose.idl.
+        Define init dictionary for joint data.
+        * testing/WebFakeXRInputController.cpp:
+        (WebCore::WebFakeXRInputController::WebFakeXRInputController):
+        (WebCore::WebFakeXRInputController::getFrameData):
+        (WebCore::WebFakeXRInputController::updateHandJoints):
+        Update m_handJoints based on joint init dictionaries.
+        * testing/WebFakeXRInputController.h:
+        * testing/WebFakeXRInputController.idl:
+        * testing/WebXRTest.cpp:
+        (WebCore::parseFeatures):
+        (WebCore::WebXRTest::simulateDeviceConnection):
+        Allow test device to specify a list of enabled features that don't need
+        further explicit consent.
+        * testing/WebXRTest.h:
+        * testing/WebXRTest.idl:
+
 2022-04-12  Gabriel Nava Marino  <[email protected]>
 
         RejectedPromiseTracker can be recreated if we are in a worker / worklet whose execution is terminating

Modified: trunk/Source/WebCore/DerivedSources-input.xcfilelist (292779 => 292780)


--- trunk/Source/WebCore/DerivedSources-input.xcfilelist	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/DerivedSources-input.xcfilelist	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1622,6 +1622,7 @@
 $(PROJECT_DIR)/testing/FakeXRBoundsPoint.idl
 $(PROJECT_DIR)/testing/FakeXRButtonStateInit.idl
 $(PROJECT_DIR)/testing/FakeXRInputSourceInit.idl
+$(PROJECT_DIR)/testing/FakeXRJointStateInit.idl
 $(PROJECT_DIR)/testing/FakeXRRigidTransformInit.idl
 $(PROJECT_DIR)/testing/FakeXRViewInit.idl
 $(PROJECT_DIR)/testing/GCObservation.idl

Modified: trunk/Source/WebCore/DerivedSources-output.xcfilelist (292779 => 292780)


--- trunk/Source/WebCore/DerivedSources-output.xcfilelist	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/DerivedSources-output.xcfilelist	2022-04-12 18:52:27 UTC (rev 292780)
@@ -835,6 +835,8 @@
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRButtonStateInit.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRInputSourceInit.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRInputSourceInit.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRJointStateInit.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRJointStateInit.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRRigidTransformInit.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRRigidTransformInit.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSFakeXRViewInit.cpp

Modified: trunk/Source/WebCore/DerivedSources.make (292779 => 292780)


--- trunk/Source/WebCore/DerivedSources.make	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/DerivedSources.make	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1462,6 +1462,7 @@
     $(WebCore)/testing/FakeXRBoundsPoint.idl \
     $(WebCore)/testing/FakeXRButtonStateInit.idl \
     $(WebCore)/testing/FakeXRInputSourceInit.idl \
+    $(WebCore)/testing/FakeXRJointStateInit.idl \
     $(WebCore)/testing/FakeXRRigidTransformInit.idl \
     $(WebCore)/testing/FakeXRViewInit.idl \
     $(WebCore)/testing/WebFakeXRDevice.idl \

Modified: trunk/Source/WebCore/Modules/webxr/WebXRFrame+HandInput.idl (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRFrame+HandInput.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRFrame+HandInput.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -28,7 +28,7 @@
     Conditional=WEBXR_HANDS,
     EnabledBySetting=WebXREnabled&WebXRHandInputModuleEnabled,
 ] partial interface WebXRFrame {
-    WebXRJointPose? getJointPose(WebXRJointSpace joint, WebXRSpace baseSpace);
+    [CallWith=CurrentDocument] WebXRJointPose? getJointPose(WebXRJointSpace joint, WebXRSpace baseSpace);
     boolean fillJointRadii(sequence<WebXRJointSpace> jointSpaces, Float32Array radii);
-    boolean fillPoses(sequence<WebXRSpace> spaces, WebXRSpace baseSpace, Float32Array transforms);
+    [CallWith=CurrentDocument] boolean fillPoses(sequence<WebXRSpace> spaces, WebXRSpace baseSpace, Float32Array transforms);
 };

Modified: trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -136,8 +136,10 @@
         return { std::nullopt };
 
     auto effectiveOrigin = space.effectiveOrigin();
+    // A space's effectiveOrigin can be null, such as a joint pose from a hand that has
+    // other missing joint poses.
     if (!effectiveOrigin)
-        return Exception { InvalidStateError };
+        return { std::nullopt };
 
     auto transform =  *baseTransform.value().inverse() * effectiveOrigin.value();
 
@@ -263,23 +265,115 @@
 #if ENABLE(WEBXR_HANDS)
 
 // https://immersive-web.github.io/webxr-hand-input/#dom-xrframe-getjointpose
-ExceptionOr<RefPtr<WebXRJointPose>> WebXRFrame::getJointPose(const WebXRJointSpace& joint, const WebXRSpace& baseSpace)
+ExceptionOr<RefPtr<WebXRJointPose>> WebXRFrame::getJointPose(const Document& document, const WebXRJointSpace& jointSpace, const WebXRSpace& baseSpace)
 {
-    UNUSED_PARAM(joint);
-    UNUSED_PARAM(baseSpace);
-    return nullptr;
+    auto populatePoseResult = populatePose(document, jointSpace, baseSpace);
+    if (populatePoseResult.hasException())
+        return populatePoseResult.releaseException();
+
+    auto populateValue = populatePoseResult.releaseReturnValue();
+    if (!populateValue)
+        return nullptr;
+
+    return RefPtr<WebXRJointPose>(WebXRJointPose::create(WebXRRigidTransform::create(populateValue->transform), populateValue->emulatedPosition, jointSpace.radius()));
 }
 
 // https://immersive-web.github.io/webxr-hand-input/#dom-xrframe-filljointradii
-ExceptionOr<bool> WebXRFrame::fillJointRadii(const Vector<RefPtr<WebXRJointSpace>>, const Float32Array&)
+ExceptionOr<bool> WebXRFrame::fillJointRadii(const Vector<RefPtr<WebXRJointSpace>>& jointSpaces, Float32Array& radii)
 {
-    return true;
+    // If frame’s active boolean is false, throw an InvalidStateError and abort these steps.
+    if (!m_active)
+        return Exception { InvalidStateError, "Frame is not active"_s };
+
+    // For each joint in the jointSpaces:
+    // If joint’s session is different from session, throw an InvalidStateError and abort these steps.
+    for (const auto& jointSpace : jointSpaces) {
+        if (!jointSpace || jointSpace->session() != m_session.ptr())
+            return Exception { InvalidStateError, "Joint space's session does not match frame's session"_s };
+    }
+
+    // If the length of jointSpaces is larger than the number of elements in radii, throw a TypeError and abort these steps.
+    if (jointSpaces.size() > radii.length())
+        return Exception { TypeError, "Unexpected length of radii array"_s };
+
+    // Let allValid be true.
+    bool allValid = true;
+
+    // For each joint in the jointSpaces:
+    // 1. Set the float value of radii at offset as follows:
+    // If the user agent can determine the poses of all the joints belonging to the joint’s hand:
+    //     Set the float value of radii at offset to that radius.
+    // Otherwise
+    //     Set the float value of radii at offset to NaN.
+    //     Set allValid to false.
+    // 2. Increase offset by 1.
+    for (size_t i = 0; i < jointSpaces.size(); ++i) {
+        if (jointSpaces[i]->handHasMissingPoses()) {
+            radii.set(i, std::numeric_limits<float>::quiet_NaN());
+            allValid = false;
+        } else
+            radii.set(i, jointSpaces[i]->radius());
+    }
+
+    return allValid;
 }
 
 // https://immersive-web.github.io/webxr-hand-input/#dom-xrframe-fillposes
-ExceptionOr<bool> WebXRFrame::fillPoses(const Vector<RefPtr<WebXRSpace>>, const WebXRSpace&, const Float32Array&)
+ExceptionOr<bool> WebXRFrame::fillPoses(const Document& document, const Vector<RefPtr<WebXRSpace>>& spaces, const WebXRSpace& baseSpace, Float32Array& transforms)
 {
-    return true;
+    // If frame’s active boolean is false, throw an InvalidStateError and abort these steps.
+    if (!m_active)
+        return Exception { InvalidStateError, "Frame is not active"_s };
+
+    // For each space in the spaces sequence:
+    // If space’s session is different from session, throw an InvalidStateError and abort these steps.
+    for (const auto& space : spaces) {
+        if (!space || space->session() != m_session.ptr())
+            return Exception { InvalidStateError, "Space's session does not match frame's session"_s };
+    }
+
+    // If baseSpace’s session is different from session, throw an InvalidStateError and abort these steps.
+    if (baseSpace.session() != m_session.ptr())
+        return Exception { InvalidStateError, "Base space's session does not match frame's session"_s };
+
+    // If the length of spaces multiplied by 16 is larger than the number of elements in transforms,
+    // throw a TypeError and abort these steps.
+    const size_t numberOfFloatsPerTransform = 16;
+    if (spaces.size() * numberOfFloatsPerTransform > transforms.length())
+        return Exception { TypeError, "Unexpected length of transforms array"_s };
+
+    // Check if poses may be reported and, if not, throw a SecurityError and abort these steps.
+    if (!m_session->posesCanBeReported(document))
+        return Exception { SecurityError, "Poses cannot be reported"_s };
+
+    // Let allValid be true.
+    bool allValid = true;
+
+    // For each space in the spaces sequence:
+    for (size_t spaceIndex = 0; spaceIndex < spaces.size(); ++spaceIndex) {
+        // 1. Populate the pose of space in baseSpace at the time represented by frame into pose.
+        auto populatePoseResult = populatePose(document, *(spaces[spaceIndex]), baseSpace);
+        if (populatePoseResult.hasException())
+            return populatePoseResult.releaseException();
+
+        // 2. If pose is null, perform the following steps:
+        // 3. Set 16 consecutive elements of the transforms array starting at offset to NaN.
+        // 4. Set allValid to false.
+        auto populateValue = populatePoseResult.releaseReturnValue();
+        if (!populateValue) {
+            for (size_t transformIndex = 0; transformIndex < numberOfFloatsPerTransform; ++transformIndex)
+                transforms.set(spaceIndex * numberOfFloatsPerTransform + transformIndex, std::numeric_limits<float>::quiet_NaN());
+            allValid = false;
+        } else {
+            // 5. If pose is not null, copy all elements from pose’s matrix member to the transforms array starting at offset.
+            // 6. Increase offset by 16.
+            auto matrix = populateValue->transform.toColumnMajorFloatArray();
+            for (size_t transformIndex = 0; transformIndex < numberOfFloatsPerTransform; ++transformIndex)
+                transforms.set(spaceIndex * numberOfFloatsPerTransform + transformIndex, matrix[transformIndex]);
+        }
+    }
+
+    return allValid;
 }
 
 #endif

Modified: trunk/Source/WebCore/Modules/webxr/WebXRFrame.h (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRFrame.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRFrame.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -60,9 +60,9 @@
     ExceptionOr<RefPtr<WebXRPose>> getPose(const Document&, const WebXRSpace&, const WebXRSpace&);
 
 #if ENABLE(WEBXR_HANDS)
-    ExceptionOr<RefPtr<WebXRJointPose>> getJointPose(const WebXRJointSpace&, const WebXRSpace&);
-    ExceptionOr<bool> fillJointRadii(const Vector<RefPtr<WebXRJointSpace>>, const Float32Array&);
-    ExceptionOr<bool> fillPoses(const Vector<RefPtr<WebXRSpace>>, const WebXRSpace&, const Float32Array&);
+    ExceptionOr<RefPtr<WebXRJointPose>> getJointPose(const Document&, const WebXRJointSpace&, const WebXRSpace&);
+    ExceptionOr<bool> fillJointRadii(const Vector<RefPtr<WebXRJointSpace>>&, Float32Array&);
+    ExceptionOr<bool> fillPoses(const Document&, const Vector<RefPtr<WebXRSpace>>&, const WebXRSpace&, Float32Array&);
 #endif
 
     void setTime(DOMHighResTimeStamp time) { m_time = time; }

Modified: trunk/Source/WebCore/Modules/webxr/WebXRHand.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRHand.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRHand.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -43,6 +43,17 @@
 WebXRHand::WebXRHand(const WebXRInputSource& inputSource)
     : m_inputSource(inputSource)
 {
+    auto* session = this->session();
+    auto* document = session ? downcast<Document>(session->scriptExecutionContext()) : nullptr;
+    if (!document)
+        return;
+
+    size_t jointCount = static_cast<size_t>(XRHandJoint::Count);
+    Vector<Ref<WebXRJointSpace>> joints;
+    joints.reserveInitialCapacity(jointCount);
+    for (size_t i = 0; i < jointCount; ++i)
+        joints.uncheckedAppend(WebXRJointSpace::create(*document, *this, static_cast<XRHandJoint>(i)));
+    m_joints = WTFMove(joints);
 }
 
 WebXRHand::~WebXRHand() = default;
@@ -49,8 +60,11 @@
 
 RefPtr<WebXRJointSpace> WebXRHand::get(const XRHandJoint& key)
 {
-    UNUSED_PARAM(key);
-    return nullptr;
+    size_t jointIndex = static_cast<size_t>(key);
+    if (jointIndex >= m_joints.size())
+        return nullptr;
+
+    return m_joints[jointIndex].ptr();
 }
 
 WebXRHand::Iterator::Iterator(WebXRHand& hand)
@@ -60,10 +74,11 @@
 
 std::optional<KeyValuePair<XRHandJoint, RefPtr<WebXRJointSpace>>> WebXRHand::Iterator::next()
 {
-    if (m_index > m_hand->m_joints.size())
+    if (m_index >= m_hand->m_joints.size())
         return std::nullopt;
 
-    return std::nullopt;
+    size_t index = m_index++;
+    return KeyValuePair<XRHandJoint, RefPtr<WebXRJointSpace>> { static_cast<XRHandJoint>(index), m_hand->m_joints[index].ptr() };
 }
 
 WebXRSession* WebXRHand::session()
@@ -70,9 +85,33 @@
 {
     if (!m_inputSource)
         return nullptr;
+
     return m_inputSource.get()->session();
 }
 
+void WebXRHand::updateFromInputSource(const PlatformXR::Device::FrameData::InputSource& inputSource)
+{
+    if (!inputSource.handJoints) {
+        m_hasMissingPoses = true;
+        return;
+    }
+
+    auto& handJoints = *(inputSource.handJoints);
+    if (handJoints.size() != m_joints.size()) {
+        m_hasMissingPoses = true;
+        return;
+    }
+
+    bool hasMissingPoses = false;
+    for (size_t i = 0; i < handJoints.size(); ++i) {
+        if (!handJoints[i])
+            hasMissingPoses = true;
+
+        m_joints[i]->updateFromJoint(handJoints[i]);
+    }
+    m_hasMissingPoses = hasMissingPoses;
 }
 
+}
+
 #endif

Modified: trunk/Source/WebCore/Modules/webxr/WebXRHand.h (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRHand.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRHand.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -36,10 +36,11 @@
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/WeakPtr.h>
 
 namespace WebCore {
 
-class WebXRHand : public RefCounted<WebXRHand> {
+class WebXRHand : public RefCounted<WebXRHand>, public CanMakeWeakPtr<WebXRHand> {
     WTF_MAKE_ISO_ALLOCATED(WebXRHand);
 public:
 
@@ -46,7 +47,7 @@
     static Ref<WebXRHand> create(const WebXRInputSource&);
     ~WebXRHand();
 
-    unsigned size() { return m_size; }
+    unsigned size() const { return m_joints.size(); }
 
     RefPtr<WebXRJointSpace> get(const XRHandJoint& key);
 
@@ -61,16 +62,17 @@
     };
     Iterator createIterator() { return Iterator(*this); }
 
-    // For GC reachablitiy.
+    // For GC reachability.
     WebXRSession* session();
 
+    bool hasMissingPoses() const { return m_hasMissingPoses; }
+    void updateFromInputSource(const PlatformXR::Device::FrameData::InputSource&);
+
 private:
     WebXRHand(const WebXRInputSource&);
 
-    unsigned m_size { 0 };
-    using HandJointToSpaceMap = HashMap<XRHandJoint, Ref<WebXRJointSpace>, DefaultHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>>;
-    HandJointToSpaceMap m_joints;
-
+    FixedVector<Ref<WebXRJointSpace>> m_joints;
+    bool m_hasMissingPoses { true };
     WeakPtr<WebXRInputSource> m_inputSource;
 };
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRInputSource.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRInputSource.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRInputSource.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -88,16 +88,19 @@
 #endif
 
 #if ENABLE(WEBXR_HANDS)
-    if (source.simulateHand) {
-        // FIXME: This currently creates an object just for use in testing.
-        // The real implementation will use actual data and only be visible
-        // if hand-tracking was requested at session start.
-        if (!m_hand)
+    if (source.handJoints) {
+        // https://www.w3.org/TR/webxr-hand-input-1/#xrinputsource-interface
+        // If the XRInputSource belongs to an XRSession that has not been requested
+        // with the "hand-tracking" feature descriptor, hand MUST be null.
+        if (!m_hand && session->isHandTrackingEnabled())
             m_hand = WebXRHand::create(*this);
-    } else
-        m_hand = nullptr;
+
+        if (m_hand)
+            m_hand->updateFromInputSource(source);
+
+        return;
+    }
 #endif
-
 }
 
 bool WebXRInputSource::requiresInputSourceChange(const InputSource& source)

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointPose.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointPose.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointPose.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -34,13 +34,14 @@
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRJointPose);
 
-Ref<WebXRJointPose> WebXRJointPose::create(Ref<WebXRRigidTransform>&& transform, bool emulatedPosition)
+Ref<WebXRJointPose> WebXRJointPose::create(Ref<WebXRRigidTransform>&& transform, bool emulatedPosition, float radius)
 {
-    return adoptRef(*new WebXRJointPose(WTFMove(transform), emulatedPosition));
+    return adoptRef(*new WebXRJointPose(WTFMove(transform), emulatedPosition, radius));
 }
 
-WebXRJointPose::WebXRJointPose(Ref<WebXRRigidTransform>&& transform, bool emulatedPosition)
+WebXRJointPose::WebXRJointPose(Ref<WebXRRigidTransform>&& transform, bool emulatedPosition, float radius)
     : WebXRPose(WTFMove(transform), emulatedPosition)
+    , m_radius(radius)
 {
 }
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointPose.h (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointPose.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointPose.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -39,13 +39,13 @@
 class WebXRJointPose : public WebXRPose {
     WTF_MAKE_ISO_ALLOCATED(WebXRJointPose);
 public:
-    static Ref<WebXRJointPose> create(Ref<WebXRRigidTransform>&&, bool emulatedPosition);
+    static Ref<WebXRJointPose> create(Ref<WebXRRigidTransform>&&, bool emulatedPosition, float radius);
     ~WebXRJointPose() = default;
 
     float radius() const { return m_radius; }
 
 private:
-    WebXRJointPose(Ref<WebXRRigidTransform>&&, bool emulatedPosition);
+    WebXRJointPose(Ref<WebXRRigidTransform>&&, bool emulatedPosition, float radius);
 
     float m_radius { 0 };
 };

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointPose.idl (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointPose.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointPose.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -26,6 +26,8 @@
 [
     Conditional=WEBXR_HANDS,
     EnabledBySetting=WebXREnabled&WebXRHandInputModuleEnabled,
+    JSGenerateToJSObject,
+    JSGenerateToNativeObject,
     SecureContext,
     Exposed=Window,
     InterfaceName=XRJointPose

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -28,6 +28,8 @@
 
 #if ENABLE(WEBXR) && ENABLE(WEBXR_HANDS)
 
+#include "WebXRFrame.h"
+#include "WebXRHand.h"
 #include "WebXRRigidTransform.h"
 #include <wtf/IsoMallocInlines.h>
 
@@ -35,22 +37,47 @@
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRJointSpace);
 
-Ref<WebXRJointSpace> WebXRJointSpace::create(Document& document, WebXRSession& session)
+Ref<WebXRJointSpace> WebXRJointSpace::create(Document& document, WebXRHand& hand, XRHandJoint jointName, std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>&& joint)
 {
-    return adoptRef(*new WebXRJointSpace(document, session));
+    return adoptRef(*new WebXRJointSpace(document, hand, jointName, WTFMove(joint)));
 }
 
-WebXRJointSpace::WebXRJointSpace(Document& document, WebXRSession& session)
+WebXRJointSpace::WebXRJointSpace(Document& document, WebXRHand& hand, XRHandJoint jointName, std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>&& joint)
     : WebXRSpace(document, WebXRRigidTransform::create())
-    , m_session(session)
+    , m_hand(hand)
+    , m_jointName(jointName)
+    , m_joint(WTFMove(joint))
 {
 }
 
 WebXRJointSpace::~WebXRJointSpace() = default;
 
+void WebXRJointSpace::updateFromJoint(const std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>& joint)
+{
+    m_joint = joint;
+}
+
+bool WebXRJointSpace::handHasMissingPoses() const
+{
+    return !m_hand || m_hand->hasMissingPoses();
+}
+
+WebXRSession* WebXRJointSpace::session() const
+{
+    return m_hand ? m_hand->session() : nullptr;
+}
+
 std::optional<TransformationMatrix> WebXRJointSpace::nativeOrigin() const
 {
-    return std::nullopt;
+    // https://immersive-web.github.io/webxr-hand-input/#xrjointspace-interface
+    // The native origin of the XRJointSpace may only be reported when native origins of
+    // all other XRJointSpaces on the same hand are being reported. When a hand is partially
+    // obscured the user agent MUST either emulate the obscured joints, or report null poses
+    // for all of the joints.
+    if (handHasMissingPoses() || !m_joint)
+        return std::nullopt;
+
+    return WebXRFrame::matrixFromPose(m_joint->pose.pose);
 }
 
 }

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.h (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -29,7 +29,6 @@
 
 #include "Document.h"
 #include "PlatformXR.h"
-#include "WebXRSession.h"
 #include "WebXRSpace.h"
 #include "XRHandJoint.h"
 #include <wtf/IsoMalloc.h>
@@ -36,24 +35,33 @@
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
-class WebXRJointSpace : public RefCounted<WebXRJointSpace>, public WebXRSpace {
+class WebXRHand;
+class WebXRSession;
+
+class WebXRJointSpace final: public RefCounted<WebXRJointSpace>, public WebXRSpace {
     WTF_MAKE_ISO_ALLOCATED(WebXRJointSpace);
 public:
-    static Ref<WebXRJointSpace> create(Document&, WebXRSession&);
+    static Ref<WebXRJointSpace> create(Document&, WebXRHand&, XRHandJoint, std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>&& = std::nullopt);
     virtual ~WebXRJointSpace();
 
     using RefCounted<WebXRJointSpace>::ref;
     using RefCounted<WebXRJointSpace>::deref;
 
-    XRHandJoint jointName() const { return XRHandJoint::Wrist; }
+    XRHandJoint jointName() const { return m_jointName; }
 
+    float radius() const { return m_joint ? m_joint->radius : 0; }
+    void updateFromJoint(const std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>&);
+    bool handHasMissingPoses() const;
+
+    WebXRSession* session() const final;
+
 private:
-    WebXRJointSpace(Document&, WebXRSession&);
+    WebXRJointSpace(Document&, WebXRHand&, XRHandJoint, std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint>&&);
 
-    WebXRSession* session() const final { return m_session.get(); };
     std::optional<TransformationMatrix> nativeOrigin() const final;
 
     void refEventTarget() final { ref(); }
@@ -61,7 +69,9 @@
 
     bool isJointSpace() const final { return true; }
 
-    WeakPtr<WebXRSession> m_session;
+    WeakPtr<WebXRHand> m_hand;
+    XRHandJoint m_jointName { XRHandJoint::Wrist };
+    std::optional<PlatformXR::Device::FrameData::InputSourceHandJoint> m_joint;
 };
 
 }

Modified: trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.idl (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRJointSpace.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -26,6 +26,7 @@
 [
     Conditional=WEBXR_HANDS,
     EnabledBySetting=WebXREnabled&WebXRHandInputModuleEnabled,
+    JSGenerateToJSObject,
     JSGenerateToNativeObject,
     SecureContext,
     Exposed=Window,

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -645,6 +645,14 @@
     return true;
 }
 
+#if ENABLE(WEBXR_HANDS)
+bool WebXRSession::isHandTrackingEnabled() const
+{
+    return m_requestedFeatures.contains(PlatformXR::SessionFeature::HandTracking);
+}
+#endif
+
+
 } // namespace WebCore
 
 #endif // ENABLE(WEBXR)

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSession.h (292779 => 292780)


--- trunk/Source/WebCore/Modules/webxr/WebXRSession.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSession.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -98,6 +98,10 @@
     const PlatformXR::Device::FrameData& frameData() const { return m_frameData; }
     const WebXRViewerSpace& viewerReferenceSpace() const { return *m_viewerReferenceSpace; }
     bool posesCanBeReported(const Document&) const;
+    
+#if ENABLE(WEBXR_HANDS)
+    bool isHandTrackingEnabled() const;
+#endif
 
 private:
     WebXRSession(Document&, WebXRSystem&, XRSessionMode, PlatformXR::Device&, FeatureList&&);

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (292779 => 292780)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1747,6 +1747,7 @@
 		5273CCB22564576F00850007 /* JSFakeXRButtonStateInit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5273CCAD2564576C00850007 /* JSFakeXRButtonStateInit.cpp */; };
 		5273CCB32564576F00850007 /* JSFakeXRRigidTransformInit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5273CCAE2564576D00850007 /* JSFakeXRRigidTransformInit.cpp */; };
 		5273CCB42564576F00850007 /* JSFakeXRViewInit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5273CCAF2564576E00850007 /* JSFakeXRViewInit.cpp */; };
+		52A0BE6627C227E10046611E /* JSFakeXRJointStateInit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52A0BE6527C227E00046611E /* JSFakeXRJointStateInit.cpp */; };
 		52B0D4BE1C57FD1E0077CE53 /* PlatformView.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B0D4BD1C57FD1E0077CE53 /* PlatformView.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		52B0D4C01C57FD660077CE53 /* VideoFullscreenChangeObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B0D4BF1C57FD660077CE53 /* VideoFullscreenChangeObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		52B0D4C21C57FF910077CE53 /* VideoFullscreenInterfaceMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B0D4C11C57FF910077CE53 /* VideoFullscreenInterfaceMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -10072,6 +10073,8 @@
 		51FB67D91AE6B5E400D06C5A /* ContentExtensionStyleSheet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentExtensionStyleSheet.cpp; sourceTree = "<group>"; };
 		51FB67DA1AE6B5E400D06C5A /* ContentExtensionStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionStyleSheet.h; sourceTree = "<group>"; };
 		52131E5A1C4F15610033F802 /* VideoFullscreenInterfaceMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VideoFullscreenInterfaceMac.mm; sourceTree = "<group>"; };
+		5263BF2027C1B5380085250C /* FakeXRJointStateInit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FakeXRJointStateInit.h; sourceTree = "<group>"; };
+		5263BF2227C1B53B0085250C /* FakeXRJointStateInit.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = FakeXRJointStateInit.idl; sourceTree = "<group>"; };
 		526724F11CB2FDF60075974D /* TextTrackRepresentationCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextTrackRepresentationCocoa.mm; sourceTree = "<group>"; };
 		526724F21CB2FDF60075974D /* TextTrackRepresentationCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTrackRepresentationCocoa.h; sourceTree = "<group>"; };
 		52688AA327D6A7DA003577A2 /* ResidentKeyRequirement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ResidentKeyRequirement.h; sourceTree = "<group>"; };
@@ -10087,6 +10090,7 @@
 		5273CCAD2564576C00850007 /* JSFakeXRButtonStateInit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFakeXRButtonStateInit.cpp; sourceTree = "<group>"; };
 		5273CCAE2564576D00850007 /* JSFakeXRRigidTransformInit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFakeXRRigidTransformInit.cpp; sourceTree = "<group>"; };
 		5273CCAF2564576E00850007 /* JSFakeXRViewInit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFakeXRViewInit.cpp; sourceTree = "<group>"; };
+		52A0BE6527C227E00046611E /* JSFakeXRJointStateInit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSFakeXRJointStateInit.cpp; path = JSFakeXRJointStateInit.cpp; sourceTree = "<group>"; };
 		52B0D4BD1C57FD1E0077CE53 /* PlatformView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformView.h; sourceTree = "<group>"; };
 		52B0D4BF1C57FD660077CE53 /* VideoFullscreenChangeObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoFullscreenChangeObserver.h; sourceTree = "<group>"; };
 		52B0D4C11C57FF910077CE53 /* VideoFullscreenInterfaceMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoFullscreenInterfaceMac.h; sourceTree = "<group>"; };
@@ -21189,6 +21193,8 @@
 				E18D83E3243F71CF009247D6 /* FakeXRButtonStateInit.idl */,
 				E18D83EA243F71D4009247D6 /* FakeXRInputSourceInit.h */,
 				E18D83DC243F71CB009247D6 /* FakeXRInputSourceInit.idl */,
+				5263BF2027C1B5380085250C /* FakeXRJointStateInit.h */,
+				5263BF2227C1B53B0085250C /* FakeXRJointStateInit.idl */,
 				E18D83E5243F71D1009247D6 /* FakeXRRigidTransformInit.h */,
 				E18D83E6243F71D1009247D6 /* FakeXRRigidTransformInit.idl */,
 				E18D83DF243F71CD009247D6 /* FakeXRViewInit.h */,
@@ -21284,6 +21290,7 @@
 				5273CCAC2564576C00850007 /* JSFakeXRBoundsPoint.cpp */,
 				5273CCAD2564576C00850007 /* JSFakeXRButtonStateInit.cpp */,
 				5273CCAA2564576B00850007 /* JSFakeXRInputSourceInit.cpp */,
+				52A0BE6527C227E00046611E /* JSFakeXRJointStateInit.cpp */,
 				5273CCAE2564576D00850007 /* JSFakeXRRigidTransformInit.cpp */,
 				5273CCAF2564576E00850007 /* JSFakeXRViewInit.cpp */,
 				51714EAE1CF6654A004723C4 /* JSGCObservation.cpp */,
@@ -38872,6 +38879,7 @@
 				5273CCB12564576F00850007 /* JSFakeXRBoundsPoint.cpp in Sources */,
 				5273CCB22564576F00850007 /* JSFakeXRButtonStateInit.cpp in Sources */,
 				5273CCB02564576F00850007 /* JSFakeXRInputSourceInit.cpp in Sources */,
+				52A0BE6627C227E10046611E /* JSFakeXRJointStateInit.cpp in Sources */,
 				5273CCB32564576F00850007 /* JSFakeXRRigidTransformInit.cpp in Sources */,
 				5273CCB42564576F00850007 /* JSFakeXRViewInit.cpp in Sources */,
 				DE5F86121FA239E7006DB63A /* JSGCObservation.cpp in Sources */,

Modified: trunk/Source/WebCore/platform/xr/PlatformXR.h (292779 => 292780)


--- trunk/Source/WebCore/platform/xr/PlatformXR.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/platform/xr/PlatformXR.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -163,7 +163,7 @@
 #if ENABLE(WEBXR_HANDS)
 
 enum class HandJoint : unsigned {
-    Wrist,
+    Wrist = 0,
     ThumbMetacarpal,
     ThumbPhalanxProximal,
     ThumbPhalanxDistal,
@@ -187,7 +187,8 @@
     PinkyFingerPhalanxProximal,
     PinkyFingerPhalanxIntermediate,
     PinkyFingerPhalanxDistal,
-    PinkyFingerTip
+    PinkyFingerTip,
+    Count
 };
 
 #endif
@@ -313,6 +314,18 @@
             template<class Decoder> static std::optional<InputSourcePose> decode(Decoder&);
         };
 
+#if ENABLE(WEBXR_HANDS)
+        struct InputSourceHandJoint {
+            InputSourcePose pose;
+            float radius { 0 };
+
+            template<class Encoder> void encode(Encoder&) const;
+            template<class Decoder> static std::optional<InputSourceHandJoint> decode(Decoder&);
+        };
+
+        using HandJointsVector = Vector<std::optional<InputSourceHandJoint>>;
+#endif
+
         struct InputSource {
             InputSourceHandle handle { 0 };
             XRHandedness handeness { XRHandedness::None };
@@ -323,8 +336,7 @@
             Vector<InputSourceButton> buttons;
             Vector<float> axes;
 #if ENABLE(WEBXR_HANDS)
-            // FIXME: Actually hold some hand data.
-            bool simulateHand { false };
+            std::optional<HandJointsVector> handJoints;
 #endif
 
             template<class Encoder> void encode(Encoder&) const;
@@ -624,7 +636,30 @@
     return inputSourcePose;
 }
 
+#if ENABLE(WEBXR_HANDS)
 template<class Encoder>
+void Device::FrameData::InputSourceHandJoint::encode(Encoder& encoder) const
+{
+    encoder << pose;
+    encoder << radius;
+}
+
+template<class Decoder>
+std::optional<Device::FrameData::InputSourceHandJoint> Device::FrameData::InputSourceHandJoint::decode(Decoder& decoder)
+{
+    std::optional<InputSourcePose> pose;
+    decoder >> pose;
+    if (!pose)
+        return std::nullopt;
+    std::optional<float> radius;
+    decoder >> radius;
+    if (!radius)
+        return std::nullopt;
+    return { { WTFMove(*pose), *radius } };
+}
+#endif
+
+template<class Encoder>
 void Device::FrameData::InputSource::encode(Encoder& encoder) const
 {
     encoder << handle;
@@ -635,6 +670,9 @@
     encoder << gripOrigin;
     encoder << buttons;
     encoder << axes;
+#if ENABLE(WEBXR_HANDS)
+    encoder << handJoints;
+#endif
 }
 
 template<class Decoder>
@@ -657,6 +695,10 @@
         return std::nullopt;
     if (!decoder.decode(source.axes))
         return std::nullopt;
+#if ENABLE(WEBXR_HANDS)
+    if (!decoder.decode(source.handJoints))
+        return std::nullopt;
+#endif
     return source;
 }
 

Modified: trunk/Source/WebCore/testing/FakeXRInputSourceInit.h (292779 => 292780)


--- trunk/Source/WebCore/testing/FakeXRInputSourceInit.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/FakeXRInputSourceInit.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -34,6 +34,10 @@
 #include "XRTargetRayMode.h"
 #include <wtf/Vector.h>
 
+#if ENABLE(WEBXR_HANDS)
+#include "FakeXRJointStateInit.h"
+#endif
+
 namespace WebCore {
 
 struct FakeXRInputSourceInit {
@@ -46,7 +50,7 @@
     Vector<FakeXRButtonStateInit> supportedButtons;
     FakeXRRigidTransformInit gripOrigin;
 #if ENABLE(WEBXR_HANDS)
-    bool simulateHand { false };
+    Vector<FakeXRJointStateInit> handJoints;
 #endif
 };
 

Modified: trunk/Source/WebCore/testing/FakeXRInputSourceInit.idl (292779 => 292780)


--- trunk/Source/WebCore/testing/FakeXRInputSourceInit.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/FakeXRInputSourceInit.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -43,5 +43,5 @@
     FakeXRRigidTransformInit gripOrigin;
     // Optional hand input. For now we don't care about the actual
     // data. We're just creating the object for testing.
-    [Conditional=WEBXR_HANDS] boolean simulateHand = false;
+    [Conditional=WEBXR_HANDS] sequence<FakeXRJointStateInit> handJoints;
 };

Copied: trunk/Source/WebCore/testing/FakeXRJointStateInit.h (from rev 292779, trunk/Source/WebCore/Modules/webxr/WebXRJointPose.idl) (0 => 292780)


--- trunk/Source/WebCore/testing/FakeXRJointStateInit.h	                        (rev 0)
+++ trunk/Source/WebCore/testing/FakeXRJointStateInit.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBXR_HANDS)
+
+#include "FakeXRRigidTransformInit.h"
+
+namespace WebCore {
+
+struct FakeXRJointStateInit {
+    FakeXRRigidTransformInit pose;
+    float radius;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEBXR_HANDS)

Copied: trunk/Source/WebCore/testing/FakeXRJointStateInit.idl (from rev 292779, trunk/Source/WebCore/Modules/webxr/WebXRJointPose.idl) (0 => 292780)


--- trunk/Source/WebCore/testing/FakeXRJointStateInit.idl	                        (rev 0)
+++ trunk/Source/WebCore/testing/FakeXRJointStateInit.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Used to update the pose and radius of a joint
+[
+    EnabledBySetting=WebXREnabled&WebXRHandInputModuleEnabled,
+    Conditional=WEBXR_HANDS,
+] dictionary FakeXRJointStateInit {
+    FakeXRRigidTransformInit pose;
+    float radius = 0.0;
+};

Modified: trunk/Source/WebCore/testing/WebFakeXRInputController.cpp (292779 => 292780)


--- trunk/Source/WebCore/testing/WebFakeXRInputController.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebFakeXRInputController.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEBXR)
 #include "WebFakeXRDevice.h"
+#include "XRHandJoint.h"
 
 namespace WebCore {
 
@@ -36,6 +37,11 @@
 using InputSourcePose = PlatformXR::Device::FrameData::InputSourcePose;
 using ButtonType = FakeXRButtonStateInit::Type;
 
+#if ENABLE(WEBXR_HANDS)
+using HandJointsVector = PlatformXR::Device::FrameData::HandJointsVector;
+using InputSourceHandJoint = PlatformXR::Device::FrameData::InputSourceHandJoint;
+#endif
+
 // https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping
 constexpr std::array<ButtonType, 5> XR_STANDARD_BUTTONS = { ButtonType::Grip, ButtonType::Touchpad, ButtonType::Thumbstick, ButtonType::OptionalButton, ButtonType::OptionalThumbstick };
 
@@ -51,13 +57,13 @@
     , m_profiles(init.profiles)
     , m_primarySelected(init.selectionStarted)
     , m_simulateSelect(init.selectionClicked)
-#if ENABLE(WEBXR_HANDS)
-    , m_simulateHand(init.simulateHand)
-#endif
 {
     setPointerOrigin(init.pointerOrigin, false);
     setGripOrigin(init.gripOrigin, false);
     setSupportedButtons(init.supportedButtons);
+#if ENABLE(WEBXR_HANDS)
+    updateHandJoints(init.handJoints);
+#endif
 }
 
 void WebFakeXRInputController::setGripOrigin(FakeXRRigidTransformInit gripOrigin, bool emulatedPosition)
@@ -110,7 +116,7 @@
     state.pointerOrigin = m_pointerOrigin;
     state.gripOrigin = m_gripOrigin;
 #if ENABLE(WEBXR_HANDS)
-    state.simulateHand = m_simulateHand;
+    state.handJoints = m_handJoints;
 #endif
 
     if (m_simulateSelect)
@@ -182,6 +188,28 @@
     return result;
 }
 
+#if ENABLE(WEBXR_HANDS)
+void WebFakeXRInputController::updateHandJoints(const Vector<FakeXRJointStateInit>& handJoints)
+{
+    if (handJoints.isEmpty() || handJoints.size() != static_cast<size_t>(XRHandJoint::Count)) {
+        m_handJoints = std::nullopt;
+        return;
+    }
+
+    HandJointsVector updatedJoints;
+    for (auto handJoint : handJoints) {
+        auto transform = WebFakeXRDevice::parseRigidTransform(handJoint.pose);
+        if (transform.hasException()) {
+            updatedJoints.append(std::nullopt);
+            continue;
+        }
+        
+        updatedJoints.append(InputSourceHandJoint { InputSourcePose { transform.releaseReturnValue(), false }, handJoint.radius });
+    }
+    m_handJoints = WTFMove(updatedJoints);
+}
+#endif // ENABLE(WEBXR_HANDS)
+
 } // namespace WebCore
 
 #endif // ENABLE(WEBXR)

Modified: trunk/Source/WebCore/testing/WebFakeXRInputController.h (292779 => 292780)


--- trunk/Source/WebCore/testing/WebFakeXRInputController.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebFakeXRInputController.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -37,6 +37,10 @@
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 
+#if ENABLE(WEBXR_HANDS)
+#include "FakeXRJointStateInit.h"
+#endif
+
 namespace WebCore {
 
 class WebFakeXRInputController final : public RefCounted<WebFakeXRInputController> {
@@ -57,6 +61,10 @@
     void setSupportedButtons(const Vector<FakeXRButtonStateInit>&);
     void updateButtonState(const FakeXRButtonStateInit&);
     bool isConnected() const { return m_connected; }
+    
+#if ENABLE(WEBXR_HANDS)
+    void updateHandJoints(const Vector<FakeXRJointStateInit>&);
+#endif
 
     PlatformXR::Device::FrameData::InputSource getFrameData();
 
@@ -80,7 +88,7 @@
     bool m_primarySelected { false };
     bool m_simulateSelect { false };
 #if ENABLE(WEBXR_HANDS)
-    bool m_simulateHand { false };
+    std::optional<PlatformXR::Device::FrameData::HandJointsVector> m_handJoints;
 #endif
 };
 

Modified: trunk/Source/WebCore/testing/WebFakeXRInputController.idl (292779 => 292780)


--- trunk/Source/WebCore/testing/WebFakeXRInputController.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebFakeXRInputController.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -73,4 +73,6 @@
     // Used to update the state of a button currently supported by the input source
     // Will not add support for that button if it is not currently supported.
     undefined updateButtonState(FakeXRButtonStateInit buttonState);
+    
+    [Conditional=WEBXR_HANDS] undefined updateHandJoints(sequence<FakeXRJointStateInit> handJoints);
 };

Modified: trunk/Source/WebCore/testing/WebXRTest.cpp (292779 => 292780)


--- trunk/Source/WebCore/testing/WebXRTest.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebXRTest.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -39,6 +39,19 @@
 
 WebXRTest::~WebXRTest() = default;
 
+static PlatformXR::Device::FeatureList parseFeatures(const Vector<JSC::JSValue>& featureList, ScriptExecutionContext& context)
+{
+    PlatformXR::Device::FeatureList features;
+    if (auto* globalObject = context.globalObject()) {
+        for (auto& feature : featureList) {
+            auto featureString = feature.toWTFString(globalObject);
+            if (auto sessionFeature = PlatformXR::parseSessionFeatureDescriptor(featureString))
+                features.append(*sessionFeature);
+        }
+    }
+    return features;
+}
+
 void WebXRTest::simulateDeviceConnection(ScriptExecutionContext& context, const FakeXRDeviceInit& init, WebFakeXRDevicePromise&& promise)
 {
     // https://immersive-web.github.io/webxr-test-api/#dom-xrtest-simulatedeviceconnection
@@ -48,16 +61,12 @@
 
         device->setViews(init.views);
 
-        PlatformXR::Device::FeatureList features;
-        if (init.supportedFeatures) {
-            if (auto* globalObject = context.globalObject()) {
-                for (auto& feature : init.supportedFeatures.value()) {
-                    auto featureString = feature.toWTFString(globalObject);
-                    if (auto sessionFeature = PlatformXR::parseSessionFeatureDescriptor(featureString))
-                        features.append(*sessionFeature);
-                }
-            }
-        }
+        PlatformXR::Device::FeatureList supportedFeatures;
+        if (init.supportedFeatures)
+            supportedFeatures = parseFeatures(init.supportedFeatures.value(), context);
+        PlatformXR::Device::FeatureList enabledFeatures;
+        if (init.enabledFeatures)
+            enabledFeatures = parseFeatures(init.enabledFeatures.value(), context);
 
         if (init.boundsCoordinates) {
             if (init.boundsCoordinates->size() < 3) {
@@ -84,8 +93,10 @@
                 supportedModes.append(XRSessionMode::ImmersiveVr);
         }
 
-        for (auto& mode : supportedModes)
-            simulatedDevice.setSupportedFeatures(mode, features);
+        for (auto& mode : supportedModes) {
+            simulatedDevice.setSupportedFeatures(mode, supportedFeatures);
+            simulatedDevice.setEnabledFeatures(mode, enabledFeatures);
+        }
 
         m_context->registerSimulatedXRDeviceForTesting(simulatedDevice);
 

Modified: trunk/Source/WebCore/testing/WebXRTest.h (292779 => 292780)


--- trunk/Source/WebCore/testing/WebXRTest.h	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebXRTest.h	2022-04-12 18:52:27 UTC (rev 292780)
@@ -46,6 +46,7 @@
         Vector<FakeXRViewInit> views;
 
         std::optional<Vector<JSC::JSValue>> supportedFeatures;
+        std::optional<Vector<JSC::JSValue>> enabledFeatures;
 
         std::optional<Vector<FakeXRBoundsPoint>> boundsCoordinates;
 

Modified: trunk/Source/WebCore/testing/WebXRTest.idl (292779 => 292780)


--- trunk/Source/WebCore/testing/WebXRTest.idl	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebCore/testing/WebXRTest.idl	2022-04-12 18:52:27 UTC (rev 292780)
@@ -61,6 +61,10 @@
     // NOTE: This is meant to emulate hardware support, not whether a feature is
     // currently available (e.g. bounds not being tracked per below)
     sequence<any> supportedFeatures;
+    
+    // The list of features that are automatically enabled and do
+    // not need further explicit consent.
+    sequence<any> enabledFeatures;
 
     // The bounds coordinates. If empty, no bounded reference space is currently tracked.
     // If not, must have at least three elements.

Modified: trunk/Source/WebKit/ChangeLog (292779 => 292780)


--- trunk/Source/WebKit/ChangeLog	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebKit/ChangeLog	2022-04-12 18:52:27 UTC (rev 292780)
@@ -1,3 +1,14 @@
+2022-04-12  Ada Chan  <[email protected]>
+
+        [WebXR] Implement the WebXRFrame methods for getting joints' poses and radii
+        https://bugs.webkit.org/show_bug.cgi?id=238968
+
+        Reviewed by Dean Jackson.
+
+        * Shared/XR/XRDeviceProxy.cpp:
+        (WebKit::XRDeviceProxy::initializeTrackingAndRendering):
+        Initialize input sources on session start.
+
 2022-04-12  Youenn Fablet  <[email protected]>
 
         Remove getsockopt from WebProcess sandboxes

Modified: trunk/Source/WebKit/Shared/XR/XRDeviceProxy.cpp (292779 => 292780)


--- trunk/Source/WebKit/Shared/XR/XRDeviceProxy.cpp	2022-04-12 18:08:56 UTC (rev 292779)
+++ trunk/Source/WebKit/Shared/XR/XRDeviceProxy.cpp	2022-04-12 18:52:27 UTC (rev 292780)
@@ -68,8 +68,21 @@
     if (sessionMode != PlatformXR::SessionMode::ImmersiveVr)
         return;
 
-    if (m_xrSystem)
-        m_xrSystem->initializeTrackingAndRendering();
+    if (!m_xrSystem)
+        return;
+
+    m_xrSystem->initializeTrackingAndRendering();
+
+    // This is called from the constructor of WebXRSession. Since sessionDidInitializeInputSources()
+    // ends up calling queueTaskKeepingObjectAlive() which refs the WebXRSession object, we
+    // should delay this call after the WebXRSession has finished construction.
+    callOnMainRunLoop([this, weakThis = WeakPtr { *this }]() {
+        if (!weakThis)
+            return;
+
+        if (trackingAndRenderingClient())
+            trackingAndRenderingClient()->sessionDidInitializeInputSources({ });
+    });    
 }
 
 void XRDeviceProxy::shutDownTrackingAndRendering()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to