summaryrefslogtreecommitdiffstats
path: root/dom/vr/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/vr/test
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/vr/test')
-rw-r--r--dom/vr/test/crashtests/crashtests.list1
-rw-r--r--dom/vr/test/crashtests/enumerate_vr_on_dying_window.html14
-rw-r--r--dom/vr/test/mochitest/VRSimulationDriver.js95
-rw-r--r--dom/vr/test/mochitest/WebVRHelpers.js19
-rw-r--r--dom/vr/test/mochitest/mochitest.toml26
-rw-r--r--dom/vr/test/mochitest/requestPresent.js74
-rw-r--r--dom/vr/test/mochitest/runVRTest.js18
-rw-r--r--dom/vr/test/mochitest/test_vrController_displayId.html57
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_canvas2d.html55
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_exitPresent.html51
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_getFrameData.html148
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html43
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html54
-rw-r--r--dom/vr/test/mochitest/test_vrDisplay_requestPresent.html130
-rw-r--r--dom/vr/test/reftest/VRSimulationDriver.js60
-rw-r--r--dom/vr/test/reftest/change_size.html168
-rw-r--r--dom/vr/test/reftest/change_size.pngbin0 -> 1439 bytes
-rw-r--r--dom/vr/test/reftest/draw_rect.html136
-rw-r--r--dom/vr/test/reftest/draw_rect.pngbin0 -> 1747 bytes
-rw-r--r--dom/vr/test/reftest/reftest.list10
-rw-r--r--dom/vr/test/reftest/webgl-util.js61
-rw-r--r--dom/vr/test/reftest/wrapper.html26
22 files changed, 1246 insertions, 0 deletions
diff --git a/dom/vr/test/crashtests/crashtests.list b/dom/vr/test/crashtests/crashtests.list
new file mode 100644
index 0000000000..8e40e0ccce
--- /dev/null
+++ b/dom/vr/test/crashtests/crashtests.list
@@ -0,0 +1 @@
+pref(dom.vr.enabled,true) pref(dom.vr.always_support_vr,true) load enumerate_vr_on_dying_window.html
diff --git a/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html b/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html
new file mode 100644
index 0000000000..2906faa79c
--- /dev/null
+++ b/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script>
+window.onload = function(){
+ var frame = document.getElementById('test_iframe');
+ var win = frame.contentWindow;
+ frame.remove();
+ win.onvrdisplayactivate = function () {}
+};
+</script></head>
+<body>
+ <iframe id="test_iframe"></iframe>
+</body>
+</html>
diff --git a/dom/vr/test/mochitest/VRSimulationDriver.js b/dom/vr/test/mochitest/VRSimulationDriver.js
new file mode 100644
index 0000000000..5b197b88e4
--- /dev/null
+++ b/dom/vr/test/mochitest/VRSimulationDriver.js
@@ -0,0 +1,95 @@
+var VRServiceTest;
+var vrMockDisplay;
+
+var VRSimulationDriver = (function () {
+ "use strict";
+
+ var AttachWebVRDisplay = function () {
+ if (vrMockDisplay) {
+ // Avoid creating multiple displays
+ return Promise.resolve(vrMockDisplay);
+ }
+ var promise = VRServiceTest.attachVRDisplay("VRDisplayTest");
+ promise.then(function (display) {
+ assert_true(display != null, "AttachWebVRDisplay should success.");
+ vrMockDisplay = display;
+ });
+
+ return promise;
+ };
+
+ var SetVRDisplayPose = function (
+ position,
+ linearVelocity,
+ linearAcceleration,
+ orientation,
+ angularVelocity,
+ angularAcceleration
+ ) {
+ vrMockDisplay.setPose(
+ position,
+ linearVelocity,
+ linearAcceleration,
+ orientation,
+ angularVelocity,
+ angularAcceleration
+ );
+ };
+
+ var SetEyeResolution = function (width, height) {
+ vrMockDisplay.setEyeResolution(width, height);
+ };
+
+ var SetEyeParameter = function (
+ eye,
+ offsetX,
+ offsetY,
+ offsetZ,
+ upDegree,
+ rightDegree,
+ downDegree,
+ leftDegree
+ ) {
+ vrMockDisplay.setEyeParameter(
+ eye,
+ offsetX,
+ offsetY,
+ offsetZ,
+ upDegree,
+ rightDegree,
+ downDegree,
+ leftDegree
+ );
+ };
+
+ var SetMountState = function (isMounted) {
+ vrMockDisplay.setMountState(isMounted);
+ };
+
+ var UpdateVRDisplay = function () {
+ vrMockDisplay.update();
+ };
+
+ var AttachVRController = function () {
+ var promise = VRServiceTest.attachVRController("VRControllerTest");
+ promise.then(function (controller) {
+ assert_true(controller != null, "AttachVRController should success.");
+ });
+
+ return promise;
+ };
+
+ var API = {
+ AttachWebVRDisplay,
+ SetVRDisplayPose,
+ SetEyeResolution,
+ SetEyeParameter,
+ SetMountState,
+ UpdateVRDisplay,
+ AttachVRController,
+
+ none: false,
+ };
+
+ return API;
+})();
diff --git a/dom/vr/test/mochitest/WebVRHelpers.js b/dom/vr/test/mochitest/WebVRHelpers.js
new file mode 100644
index 0000000000..e19294a236
--- /dev/null
+++ b/dom/vr/test/mochitest/WebVRHelpers.js
@@ -0,0 +1,19 @@
+var WebVRHelpers = (function () {
+ "use strict";
+
+ var RequestPresentOnVRDisplay = function (vrDisplay, vrLayers, callback) {
+ if (callback) {
+ callback();
+ }
+
+ return vrDisplay.requestPresent(vrLayers);
+ };
+
+ var API = {
+ RequestPresentOnVRDisplay,
+
+ none: false,
+ };
+
+ return API;
+})();
diff --git a/dom/vr/test/mochitest/mochitest.toml b/dom/vr/test/mochitest/mochitest.toml
new file mode 100644
index 0000000000..ce2d4347d3
--- /dev/null
+++ b/dom/vr/test/mochitest/mochitest.toml
@@ -0,0 +1,26 @@
+[DEFAULT]
+skip-if = ["true"] # Dependencies for re-enabling these are tracked by meta bug 1555185
+support-files = [
+ "VRSimulationDriver.js",
+ "requestPresent.js",
+ "runVRTest.js",
+ "WebVRHelpers.js",
+]
+
+["test_vrController_displayId.html"]
+# Enable Linux after Bug 1310655 # TIMED_OUT for Android.
+
+["test_vrDisplay_canvas2d.html"]
+# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+
+["test_vrDisplay_exitPresent.html"]
+# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+
+["test_vrDisplay_getFrameData.html"]
+# Enable Linux after Bug 1310655, enable Android after Bug 1348246
+
+["test_vrDisplay_onvrdisplayconnect.html"]
+
+["test_vrDisplay_onvrdisplaydeactivate_crosscontent.html"]
+
+["test_vrDisplay_requestPresent.html"]
diff --git a/dom/vr/test/mochitest/requestPresent.js b/dom/vr/test/mochitest/requestPresent.js
new file mode 100644
index 0000000000..a2f9dd4d11
--- /dev/null
+++ b/dom/vr/test/mochitest/requestPresent.js
@@ -0,0 +1,74 @@
+// requestPresent.js
+//
+// This file provides helpers for testing VRDisplay requestPresent.
+
+function attachVRDisplay(test) {
+ assert_equals(
+ typeof navigator.getVRDisplays,
+ "function",
+ "'navigator.getVRDisplays()' must be defined."
+ );
+ return VRSimulationDriver.AttachWebVRDisplay();
+}
+
+function setupVRDisplay(test) {
+ assert_equals(
+ typeof navigator.getVRDisplays,
+ "function",
+ "'navigator.getVRDisplays()' must be defined."
+ );
+ return VRSimulationDriver.AttachWebVRDisplay()
+ .then(() => {
+ return navigator.getVRDisplays();
+ })
+ .then(displays => {
+ assert_equals(
+ displays.length,
+ 1,
+ "displays.length must be one after attach."
+ );
+ vrDisplay = displays[0];
+ return validateNewVRDisplay(test, vrDisplay);
+ });
+}
+
+// Validate the settings off a freshly created VRDisplay (prior to calling
+// requestPresent).
+function validateNewVRDisplay(test, display) {
+ assert_true(
+ display.capabilities.canPresent,
+ "display.capabilities.canPresent must always be true for HMDs."
+ );
+ assert_equals(
+ display.capabilities.maxLayers,
+ 1,
+ "display.capabilities.maxLayers must always be 1 when display.capabilities.canPresent is true for current spec revision."
+ );
+ assert_false(
+ display.isPresenting,
+ "display.isPresenting must be false before calling requestPresent."
+ );
+ assert_equals(
+ display.getLayers().length,
+ 0,
+ "display.getLayers() should have no layers if not presenting."
+ );
+ var promise = display.exitPresent();
+ return promise_rejects(test, null, promise);
+}
+
+// Validate the settings off a VRDisplay after requestPresent promise is
+// rejected or after exitPresent is fulfilled.
+function validateDisplayNotPresenting(test, display) {
+ assert_false(
+ display.isPresenting,
+ "display.isPresenting must be false if requestPresent is rejected or after exitPresent is fulfilled."
+ );
+ assert_equals(
+ display.getLayers().length,
+ 0,
+ "display.getLayers() should have no layers if requestPresent is rejected or after exitPresent is fulfilled."
+ );
+ var promise = display.exitPresent();
+ return promise_rejects(test, null, promise);
+}
diff --git a/dom/vr/test/mochitest/runVRTest.js b/dom/vr/test/mochitest/runVRTest.js
new file mode 100644
index 0000000000..c15003a6c3
--- /dev/null
+++ b/dom/vr/test/mochitest/runVRTest.js
@@ -0,0 +1,18 @@
+function runVRTest(callback) {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["dom.vr.enabled", true],
+ ["dom.vr.puppet.enabled", true],
+ ["dom.vr.require-gesture", false],
+ ["dom.vr.test.enabled", true],
+ ["dom.vr.display.enumerate.interval", 0],
+ ["dom.vr.controller.enumerate.interval", 0],
+ ],
+ },
+ () => {
+ VRServiceTest = navigator.requestVRServiceTest();
+ callback();
+ }
+ );
+}
diff --git a/dom/vr/test/mochitest/test_vrController_displayId.html b/dom/vr/test/mochitest/test_vrController_displayId.html
new file mode 100644
index 0000000000..e69e3920d2
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrController_displayId.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>VRController DisplayId</title>
+ <meta name="timeout" content="long"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="runVRTest.js"></script>
+ </head>
+ <body>
+ <script>
+ "use strict";
+ var vrDisplay;
+ var controllerCount = 0;
+
+ function addController() {
+ promise_test((test) => {
+ return VRSimulationDriver.AttachVRController().then((controller) => {
+ controller.newButtonEvent(0, true);
+ });
+ }, "Finish to add VRController.");
+ }
+
+ function listenControllerEvents() {
+ async_test(function(t) {
+ window.addEventListener("gamepadbuttondown", function(e) {
+ assert_equals(e.gamepad.displayId, vrDisplay.displayId, "gamepad.displayId should be equal to vrDisplay.");
+ assert_equals(e.gamepad.id, "Puppet Gamepad", "gamepad.id must be equal to 'Puppet Gamepad'.");
+ ++controllerCount;
+ if (controllerCount == 2) {
+ t.done();
+ }
+ });
+ }, "Finish to verify VRController.displayId.");
+ }
+
+ function startTest() {
+ promise_test((test) => {
+ listenControllerEvents();
+ return VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ return navigator.getVRDisplays().then((displays) => {
+ vrDisplay = displays[0];
+ assert_equals(displays.length, 1, "displays.length must be one after attach.");
+ assert_equals(vrDisplay.displayName, "Puppet HMD", "display.displayName must be equal to 'Puppet HMD'.");
+ addController();
+ addController();
+ });
+ });
+ }, "Finish to add VRDisplay.");
+ }
+
+ runVRTest(startTest);
+ </script>
+ </body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html b/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html
new file mode 100644
index 0000000000..4d20e21352
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>VRDisplay Canvas2D</title>
+ <meta name="timeout" content="long"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="runVRTest.js"></script>
+ </head>
+ <body>
+ <script>
+ "use strict";
+ var vrDisplay;
+
+ function requestPresentTest() {
+ async_test(function (test) {
+ vrDisplay.requestAnimationFrame(callback);
+
+ function callback() {
+ vrDisplay.resetPose();
+ vrDisplay.getLayers();
+ vrDisplay.submitFrame();
+ vrDisplay.getEyeParameters("right");
+ test.done();
+ }
+ }, "Finish WebVR Canvas2D requestPresentTest.");
+ }
+
+ function startTest() {
+ promise_test((test) => {
+ var canvas = document.createElement('canvas');
+ (document.body || document.documentElement).appendChild(canvas);
+ var context = canvas.getContext('2d');
+ var img = document.createElement('img');
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACw=";
+
+ return VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ return navigator.getVRDisplays().then((displays) => {
+ assert_equals(displays.length, 1, "displays.length must be one after attach.");
+ vrDisplay = displays[0];
+ var frameData = new VRFrameData();
+ return vrDisplay.requestPresent([{source: canvas}]).then(() => {
+ requestPresentTest();
+ });
+ });
+ });
+ }, "Finish running WebVR Canvas2D test.");
+ }
+
+ runVRTest(startTest);
+ </script>
+ </body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html b/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html
new file mode 100644
index 0000000000..dc2c700723
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>VRDisplay ExitPresent</title>
+ <meta name="timeout" content="long"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="runVRTest.js"></script>
+ </head>
+ <body>
+ <script>
+ function testExitPresentOnOtherIframe(content) {
+ return content.navigator.getVRDisplays().then((displays) => {
+ content.vrDisplay = displays[0];
+ return content.vrDisplay.exitPresent();
+ });
+ }
+ var initVRPresentation = function(content) {
+ return VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ return content.navigator.getVRDisplays().then((displays) => {
+ content.vrDisplay = displays[0];
+ content.canvas = content.document.createElement("canvas");
+ content.canvas.id = "vrCanvas";
+ return content.vrDisplay.requestPresent([{source:content.canvas}]);
+ });
+ });
+ }
+ function startTest() {
+ var ifr1 = document.getElementById("iframe1");
+ var ifr2 = document.getElementById("iframe2");
+ var frame1 = ifr1.contentWindow;
+ var frame2 = ifr2.contentWindow;
+ promise_test((test) => {
+ return VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ return initVRPresentation(frame1).then(() => {
+ promise_test((test) => {
+ return promise_rejects(test, null, testExitPresentOnOtherIframe(frame2));
+ }, "We cannot exit VR presentation established by another content, this promise is expected to be rejected.")
+ });
+ });
+ }, "Finish running WebVR exitPresent test.");
+ }
+ runVRTest(startTest);
+ </script>
+
+ <iframe id="iframe1"></iframe>
+ <iframe id="iframe2"></iframe>
+ </body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html b/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html
new file mode 100644
index 0000000000..c8a986ae8e
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>VRDisplay GetFrameData</title>
+ <meta name="timeout" content="long"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="WebVRHelpers.js"></script>
+ <script src="requestPresent.js"></script>
+ <script src="runVRTest.js"></script>
+</head>
+<body id="body">
+ <canvas id="webglCanvas"></canvas>
+ <script>
+ "use strict";
+ var vrDisplay;
+ var vrRAF;
+ var canvas = document.getElementById('webglCanvas');
+ function startTest() {
+ promise_test((test) => {
+ return attachVRDisplay(test).then(() => {
+ VRSimulationDriver.SetEyeResolution(1332, 1586);
+ VRSimulationDriver.SetEyeParameter("left", -0.029, 0, 0, 41.65, 35.57, 48.00, 43.97);
+ VRSimulationDriver.SetEyeParameter("right", 0.029, 0, 0, 41.65, 43.97, 48.00, 35.57);
+ var poseOrient = new Float32Array([-0.188, -0.007, 0.045, -0.980]);
+ var posePos = new Float32Array([-0.161, 0.076, -0.250]);
+ var poseAngVel = new Float32Array([0.008, -0.002, -0.006]);
+ var poseAngAcc = new Float32Array([3.404, -1.469, -5.901]);
+ var poseLinVel = new Float32Array([0.001, -0.003, -0.002]);
+ var poseLinAcc = new Float32Array([0.007, 0.068, -0.052]);
+ VRSimulationDriver.SetVRDisplayPose(posePos, poseLinVel, poseLinAcc,
+ poseOrient, poseAngVel, poseAngAcc);
+ VRSimulationDriver.UpdateVRDisplay();
+ }).then(() => {
+ return promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay,
+ [{ source: canvas }]);
+ }).then(() => {
+ assert_true(vrDisplay.isPresenting, "vrDisplay.isPresenting must be true if requestPresent is fulfilled.");
+ assert_equals(vrDisplay.getLayers().length, 1, "vrDisplay.getLayers() should return one layer.");
+
+ verifyFrameData();
+ })
+ }, "WebVR requestPresent fulfilled");
+ })
+ }, "Finish setting up VR test data.");
+
+ function verifyFrameData() {
+ async_test(function (test) {
+ navigator.getVRDisplays().then((displays) => {
+ assert_equals(displays.length, 1, "displays.length must be one after attach.");
+ vrDisplay = displays[0];
+ vrDisplay.requestAnimationFrame(callback);
+
+ function callback() {
+ var frameData1 = new VRFrameData();
+ vrDisplay.getFrameData(frameData1);
+
+ // We insert a new frame to confirm we still can get
+ // the same data as the last getter.
+ insertNewFrameData();
+
+ var frameData2 = new VRFrameData();
+ vrDisplay.getFrameData(frameData2);
+
+ assert_equals(frameData1.timestamp, frameData2.timestamp,
+ "frameData.timestamp at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(frameData1.leftProjectionMatrix,
+ frameData2.leftProjectionMatrix),
+ "frameData.leftProjectionMatrix at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(frameData1.leftViewMatrix,
+ frameData2.leftViewMatrix),
+ "frameData.leftViewMatrix at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(frameData1.rightProjectionMatrix,
+ frameData2.rightProjectionMatrix),
+ "frameData.rightProjectionMatrix at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(frameData1.rightViewMatrix,
+ frameData2.rightViewMatrix),
+ "frameData.rightViewMatrix at a frame should be equal.");
+
+ var pose1 = frameData1.pose;
+ var pose2 = frameData2.pose;
+ assert_true(checkValueInFloat32Array(pose1.position,
+ pose2.position),
+ "pose.position at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(pose1.linearVelocity,
+ pose2.linearVelocity),
+ "pose.linearVelocity at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(pose1.linearAcceleration,
+ pose2.linearAcceleration),
+ "pose.linearAcceleration at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(pose1.orientation,
+ pose2.orientation),
+ "pose.orientation at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(pose1.angularVelocity,
+ pose2.angularVelocity),
+ "pose.angularVelocity at a frame should be equal.");
+
+ assert_true(checkValueInFloat32Array(pose1.angularAcceleration,
+ pose2.angularAcceleration),
+ "pose.angularAcceleration at a frame should be equal.");
+ test.done();
+ }
+ });
+ }, "WebVR returns the same frameData within a frame fulfilled");
+ }
+
+ function insertNewFrameData() {
+ var poseOrient = new Float32Array([-0.208, -0.017, 0.055, -0.930]);
+ var posePos = new Float32Array([-0.261, 0.036, -0.150]);
+ var poseAngVel = new Float32Array([0.018, -0.001, -0.003]);
+ var poseAngAcc = new Float32Array([1.504, -1.339, -4.901]);
+ var poseLinVel = new Float32Array([0.002, -0.001, -0.003]);
+ var poseLinAcc = new Float32Array([0.017, 0.061, -0.022]);
+ VRSimulationDriver.SetVRDisplayPose(posePos, poseLinVel, poseLinAcc,
+ poseOrient, poseAngVel, poseAngAcc);
+ VRSimulationDriver.UpdateVRDisplay();
+ }
+
+ function checkValueInFloat32Array(array1, array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ var index = 0;
+ while (index < array2.length) {
+ if (array1[index] != array2[index]) {
+ return false;
+ }
+ ++index;
+ }
+ return true;
+ }
+ }
+
+ runVRTest(startTest);
+ </script>
+</body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html
new file mode 100644
index 0000000000..e1912c7c41
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html
@@ -0,0 +1,43 @@
+<html>
+ <head>
+ <title>VRDisplay onvrdisplayconnect test</title>
+ <meta name="timeout" content="long"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="WebVRHelpers.js"></script>
+ <script src="requestPresent.js"></script>
+ <script src="runVRTest.js"></script>
+ </head>
+ <body>
+ <script>
+
+ function eventAfterConnectedTest() {
+ async_test(function (test) {
+ window.addEventListener("vrdisplayconnect", () => {
+ test.done();
+ });
+ }, "vrdisplayconnect should fire as soon as content listens for it, \
+ even if the VR display was already connected.");
+ }
+
+ function startTest() {
+ promise_test((test) => {
+ return attachVRDisplay(test).then(() => {
+ return promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ VRSimulationDriver.UpdateVRDisplay();
+ eventAfterConnectedTest();
+ VRSimulationDriver.UpdateVRDisplay();
+ });
+ });
+ });
+ });
+ }
+
+ runVRTest(startTest);
+ </script>
+ <iframe id="iframe1"></iframe>
+ </body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html
new file mode 100644
index 0000000000..6c58e5efd1
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html
@@ -0,0 +1,54 @@
+<html>
+ <head>
+ <title>VRDisplay onvrdisplaydeactivate Crosscontent test</title>
+ <meta name="timeout" content="long"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="WebVRHelpers.js"></script>
+ <script src="requestPresent.js"></script>
+ <script src="runVRTest.js"></script>
+ </head>
+ <body>
+ <canvas id="vrCanvas"></canvas>
+ <script>
+
+ function startTest() {
+ var canvas = document.getElementById("vrCanvas");
+ var iframe1 = document.getElementById("iframe1").contentWindow;
+ var t = async_test("vrdisplaydeactivate crosscontent test");
+
+ window.addEventListener("vrdisplaydeactivate", () => {
+ t.step(() => {
+ assert_true(vrDisplay.isPresenting,
+ "VRDisplay should be still presenting now without being affected by the event.");
+ t.done();
+ });
+ });
+
+ iframe1.addEventListener("vrdisplaydeactivate", () => {
+ t.unreached_func("vrdisplaydeactivate should not be received by other iframe.");
+ });
+
+ promise_test((test) => {
+ return attachVRDisplay(test).then(() => {
+ return promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ VRSimulationDriver.SetMountState(true);
+ VRSimulationDriver.UpdateVRDisplay();
+ return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{source: canvas}]);
+ }).then(() => {
+ VRSimulationDriver.SetMountState(false);
+ VRSimulationDriver.UpdateVRDisplay();
+ });
+ });
+ });
+ });
+ }
+
+ runVRTest(startTest);
+ </script>
+ <iframe id="iframe1"></iframe>
+ </body>
+</html>
diff --git a/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html b/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html
new file mode 100644
index 0000000000..4d021b6e0f
--- /dev/null
+++ b/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>VRDisplay RequestPresent</title>
+ <meta name="timeout" content="long"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="VRSimulationDriver.js"></script>
+ <script src="WebVRHelpers.js"></script>
+ <script src="requestPresent.js"></script>
+ <script src="runVRTest.js"></script>
+</head>
+<body id="body">
+ <canvas id="webglCanvas"></canvas>
+ <div id="testDiv"></div>
+ <script>
+ "use strict";
+ var vrDisplay;
+ var canvas = document.getElementById('webglCanvas');
+ var div = document.getElementById('testDiv');
+ function startTest() {
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{}]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with empty frames");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: [0.0, 0.0] }]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with incorrect bounds (bounds arrays must be 0 or 4 long)");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: div }]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with invalid source (must be canvas element)");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: [div] }]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with invalid bounds data type (must be able to convert to float)");
+
+ const invalidBounds = [
+ [2.0, 0.0, 0.0, 0.0],
+ [0.0, 2.0, 0.0, 0.0],
+ [0.0, 0.0, 2.0, 0.0],
+ [0.0, 0.0, 0.0, 2.0],
+ [-1.0, 0.0, 0.0, 0.0],
+ [0.0, -1.0, 0.0, 0.0],
+ [0.0, 0.0, -1.0, 0.0],
+ [0.0, 0.0, 0.0, -1.0]];
+
+ invalidBounds.forEach((bound) => {
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: bound }]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with bounds in invalid range: [" + bound + "]");
+ });
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ var promise = vrDisplay.requestPresent({ source: canvas });
+ return promise_rejects(test, null, promise);
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected without user initiated action");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }, { source: canvas }]));
+ }).then(() => {
+ return validateDisplayNotPresenting(test, vrDisplay);
+ });
+ }, "WebVR requestPresent rejected with more frames than max layers");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ function requestAgain() {
+ // Callback for immediate requestPresent call for further testing.
+ // Cache this promise on global object since it seems to be the only object
+ // in scope across calls.
+ window.promiseSecond = vrDisplay.requestPresent([{ source: canvas }]);
+ }
+ return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }], requestAgain);
+ }).then(() => {
+ // First promise succeeded
+ assert_true(vrDisplay.isPresenting, "First promise should successfully fulfill");
+ // Now, validate that the subsequent requestPresent was rejected
+ return promise_rejects(test, null, window.promiseSecond);
+ }).then(() => {
+ delete window.promiseSecond;
+ assert_true(vrDisplay.isPresenting, "Should still be presenting after rejected second promise");
+ return vrDisplay.exitPresent();
+ });
+ }, "WebVR requestPresent fails while another one is in progress");
+
+ promise_test((test) => {
+ return setupVRDisplay(test).then(() => {
+ return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }]);
+ }).then(() => {
+ assert_true(vrDisplay.isPresenting, "vrDisplay.isPresenting must be true if requestPresent is fulfilled.");
+ assert_equals(vrDisplay.getLayers().length, 1, "vrDisplay.getLayers() should return one layer.");
+ return vrDisplay.exitPresent();
+ }).then(() => {
+ assert_false(vrDisplay.isPresenting, "vrDisplay.isPresenting must be false if exitPresent is fulfilled.");
+ // exitPresent() should reject since we are no longer presenting.
+ return promise_rejects(test, null, vrDisplay.exitPresent());
+ });
+ }, "WebVR requestPresent fulfilled");
+ }
+
+ runVRTest(startTest);
+ </script>
+</body>
+</html>
diff --git a/dom/vr/test/reftest/VRSimulationDriver.js b/dom/vr/test/reftest/VRSimulationDriver.js
new file mode 100644
index 0000000000..971cdb8626
--- /dev/null
+++ b/dom/vr/test/reftest/VRSimulationDriver.js
@@ -0,0 +1,60 @@
+
+var VRServiceTest;
+var vrMockDisplay;
+
+var VRSimulationDriver = (function() {
+"use strict";
+
+var AttachWebVRDisplay = function() {
+ if (vrMockDisplay) {
+ // Avoid creating multiple displays
+ return Promise.resolve(vrMockDisplay);
+ }
+ var promise = VRServiceTest.attachVRDisplay("VRDisplayTest");
+ promise.then(function (display) {
+ vrMockDisplay = display;
+ });
+
+ return promise;
+};
+
+var SetVRDisplayPose = function(position,
+ linearVelocity, linearAcceleration,
+ orientation, angularVelocity,
+ angularAcceleration) {
+ vrMockDisplay.setPose(position, linearVelocity, linearAcceleration,
+ orientation, angularVelocity, angularAcceleration);
+};
+
+var SetEyeResolution = function(width, height) {
+ vrMockDisplay.setEyeResolution(width, height);
+}
+
+var SetEyeParameter = function(eye, offsetX, offsetY, offsetZ,
+ upDegree, rightDegree, downDegree, leftDegree) {
+ vrMockDisplay.setEyeParameter(eye, offsetX, offsetY, offsetZ, upDegree, rightDegree,
+ downDegree, leftDegree);
+}
+
+var SetMountState = function(isMounted) {
+ vrMockDisplay.setMountState(isMounted);
+}
+
+var UpdateVRDisplay = function() {
+ vrMockDisplay.update();
+}
+
+var API = {
+ AttachWebVRDisplay: AttachWebVRDisplay,
+ SetVRDisplayPose: SetVRDisplayPose,
+ SetEyeResolution: SetEyeResolution,
+ SetEyeParameter: SetEyeParameter,
+ SetMountState: SetMountState,
+ UpdateVRDisplay: UpdateVRDisplay,
+
+ none: false
+};
+
+return API;
+
+}()); \ No newline at end of file
diff --git a/dom/vr/test/reftest/change_size.html b/dom/vr/test/reftest/change_size.html
new file mode 100644
index 0000000000..87d59f6a3c
--- /dev/null
+++ b/dom/vr/test/reftest/change_size.html
@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<meta charset='UTF-8'>
+<!-- Viewport size change in WebGL and submit it to the VR device as a base64 image.
+If this fails, something is seriously wrong. -->
+<html class="reftest-wait">
+<head>
+ <script type='text/javascript' src='webgl-util.js'></script>
+ <script type='text/javascript' src="VRSimulationDriver.js"></script>
+ <script id="vs" type="x-shader/x-vertex">
+ attribute vec2 aVertCoord;
+
+ void main(void) {
+ gl_Position = vec4(aVertCoord, 0.0, 1.0);
+ }
+ </script>
+ <script id="fs" type="x-shader/x-fragment">
+ precision mediump float;
+
+ void main(void) {
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }
+ </script>
+ <script type='text/javascript'>
+ 'use strict';
+
+ var submitResult = null;
+ var vrDisplay = null;
+ var webglCanvas = null;
+ var gl = null;
+ var prog = null;
+ var img = null;
+ // The resolution is 540 : 300 (the ratio of 2160 * 1200, like Vive and Oculus)
+ const eyeWidth = 270;
+ const eyeHeight = 300;
+
+ function setStatus(text) {
+ var elem = document.getElementById('status');
+ elem.innerHTML = text;
+ }
+
+ function initVRMock() {
+ VRServiceTest = navigator.requestVRServiceTest();
+ if (!VRServiceTest) {
+ setStatus('VRServiceTest get failed.');
+ return;
+ }
+
+ VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ VRSimulationDriver.SetEyeResolution(eyeWidth, eyeHeight);
+ VRSimulationDriver.UpdateVRDisplay();
+ }).then(() => {
+ // Looking for VR displays
+ if (navigator.getVRDisplays) {
+ submitResult = new VRSubmitFrameResult();
+ navigator.getVRDisplays().then(function (displays) {
+ if (displays.length > 0) {
+ window.addEventListener('vrdisplaypresentchange', onVRPresentChange, false);
+
+ vrDisplay = displays[0];
+ vrDisplay.requestPresent([{ source: webglCanvas }]);
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ }
+ });
+ }
+ });
+ }
+
+ function onVRPresentChange() {
+ if (vrDisplay && vrDisplay.isPresenting) {
+ const leftEye = vrDisplay.getEyeParameters("left");
+ const rightEye = vrDisplay.getEyeParameters("right");
+
+ if (leftEye.renderWidth != rightEye.renderWidth ||
+ leftEye.renderWidth != eyeWidth) {
+ setStatus('renderWidth is not equal to eyeWidth.');
+ }
+
+ if (leftEye.renderHeight != rightEye.renderHeight ||
+ leftEye.renderHeight != eyeHeight) {
+ setStatus('renderHeight is not equal to eyeHeight.');
+ }
+ webglCanvas.width = leftEye.renderWidth * 2;
+ webglCanvas.height = leftEye.renderHeight;
+ }
+ }
+
+ function onAnimationFrame() {
+ if (!vrDisplay.isPresenting) {
+ return;
+ }
+
+ gl.clearColor(0.0, 1.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Presenting render a stereo view.
+ gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ // Indicate VRDisplay we're done rendering.
+ vrDisplay.submitFrame();
+ if (vrDisplay.getSubmitFrameResult(submitResult)) {
+ if (!img) {
+ img = document.createElement("img");
+ img.onload = function(){
+ // img width will not be eyeWidth * 2 (540), it would
+ // be 544. It is because D3D11 CopyResource changes
+ // the destination image size.
+ if ((img.height == eyeHeight)) {
+ webglCanvas.style.display = 'none';
+ vrDisplay.exitPresent();
+ setTimeout(testComplete, 0);
+ }
+ };
+ img.src = submitResult.base64Image;
+ document.body.appendChild(img);
+ } else {
+ img.src = submitResult.base64Image;
+ }
+ }
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function runTest() {
+ webglCanvas = document.getElementById('canvas');
+ gl = webglCanvas.getContext('webgl');
+ if (!gl) {
+ setStatus('WebGL context creation failed.');
+ return;
+ }
+ gl.disable(gl.DEPTH_TEST);
+ prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
+ if (!prog) {
+ setStatus('Program linking failed.');
+ return;
+ }
+ prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord");
+
+ var vertCoordArr = new Float32Array([
+ -0.5, -0.5,
+ 0.5, -0.5,
+ -0.5, 0.5,
+ 0.5, 0.5,
+ ]);
+ var vertCoordBuff = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
+ gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
+ gl.useProgram(prog);
+ gl.enableVertexAttribArray(prog.aVertCoord);
+ gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0);
+
+ initVRMock();
+ }
+
+ function testComplete() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</head>
+
+<body onload='runTest();'>
+ <canvas id='canvas' width='128' height='128'></canvas>
+ <div id='status'></div>
+</body>
+
+</html>
diff --git a/dom/vr/test/reftest/change_size.png b/dom/vr/test/reftest/change_size.png
new file mode 100644
index 0000000000..fe03114b20
--- /dev/null
+++ b/dom/vr/test/reftest/change_size.png
Binary files differ
diff --git a/dom/vr/test/reftest/draw_rect.html b/dom/vr/test/reftest/draw_rect.html
new file mode 100644
index 0000000000..acb8580c1b
--- /dev/null
+++ b/dom/vr/test/reftest/draw_rect.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset='UTF-8'>
+<!-- Draw rect in WebGL and submit it to the VR device as a base64 image.
+If this fails, something is seriously wrong. -->
+<html class="reftest-wait">
+<head>
+ <script type='text/javascript' src='webgl-util.js'></script>
+ <script type='text/javascript' src="VRSimulationDriver.js"></script>
+ <script id="vs" type="x-shader/x-vertex">
+ attribute vec2 aVertCoord;
+
+ void main(void) {
+ gl_Position = vec4(aVertCoord, 0.0, 1.0);
+ }
+ </script>
+ <script id="fs" type="x-shader/x-fragment">
+ precision mediump float;
+
+ void main(void) {
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }
+ </script>
+ <script type='text/javascript'>
+ 'use strict';
+
+ var submitResult = null;
+ var vrDisplay = null;
+ var webglCanvas = null;
+ var gl = null;
+ var prog = null;
+ var img = null;
+
+ function setStatus(text) {
+ var elem = document.getElementById('status');
+ elem.innerHTML = text;
+ }
+
+ function initVRMock() {
+ VRServiceTest = navigator.requestVRServiceTest();
+ if (!VRServiceTest) {
+ setStatus('VRServiceTest get failed.');
+ return;
+ }
+
+ VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ // Looking for VR displays
+ if (navigator.getVRDisplays) {
+ submitResult = new VRSubmitFrameResult();
+ navigator.getVRDisplays().then(function (displays) {
+ if (displays.length > 0) {
+ vrDisplay = displays[0];
+ vrDisplay.requestPresent([{ source: webglCanvas }]);
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ }
+ });
+ }
+ });
+ }
+
+ function onAnimationFrame() {
+ if (!vrDisplay.isPresenting) {
+ return;
+ }
+
+ gl.clearColor(0.0, 1.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Presenting render a stereo view.
+ gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ // Indicate VRDisplay we're done rendering.
+ vrDisplay.submitFrame();
+ if (vrDisplay.getSubmitFrameResult(submitResult)) {
+ if (!img) {
+ img = document.createElement("img");
+ img.onload = function(){
+ webglCanvas.style.display = 'none';
+ vrDisplay.exitPresent();
+ setTimeout(testComplete, 0);
+ };
+ img.src = submitResult.base64Image;
+ document.body.appendChild(img);
+ } else {
+ img.src = submitResult.base64Image;
+ }
+ }
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function runTest() {
+ webglCanvas = document.getElementById('canvas');
+ gl = webglCanvas.getContext('webgl');
+ if (!gl) {
+ setStatus('WebGL context creation failed.');
+ return;
+ }
+ gl.disable(gl.DEPTH_TEST);
+ prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
+ if (!prog) {
+ setStatus('Program linking failed.');
+ return;
+ }
+ prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord");
+
+ var vertCoordArr = new Float32Array([
+ -0.5, -0.5,
+ 0.5, -0.5,
+ -0.5, 0.5,
+ 0.5, 0.5,
+ ]);
+ var vertCoordBuff = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
+ gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
+ gl.useProgram(prog);
+ gl.enableVertexAttribArray(prog.aVertCoord);
+ gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0);
+
+ initVRMock();
+ }
+
+ function testComplete() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</head>
+
+<body onload='runTest();'>
+ <canvas id='canvas' width='256' height='256'></canvas>
+ <div id='status'></div>
+</body>
+
+</html>
diff --git a/dom/vr/test/reftest/draw_rect.png b/dom/vr/test/reftest/draw_rect.png
new file mode 100644
index 0000000000..0f4d24a0d1
--- /dev/null
+++ b/dom/vr/test/reftest/draw_rect.png
Binary files differ
diff --git a/dom/vr/test/reftest/reftest.list b/dom/vr/test/reftest/reftest.list
new file mode 100644
index 0000000000..c1ca292c71
--- /dev/null
+++ b/dom/vr/test/reftest/reftest.list
@@ -0,0 +1,10 @@
+# WebVR Reftests
+# Please confirm there is no other VR display connected. Otherwise, VRPuppetDisplay can't be attached.
+defaults pref(dom.vr.enabled,true) pref(dom.vr.puppet.enabled,true) pref(dom.vr.test.enabled,true) pref(dom.vr.require-gesture,false) pref(dom.vr.puppet.submitframe,1) pref(dom.vr.display.rafMaxDuration,200) pref(dom.vr.display.enumerate.interval,0) pref(dom.vr.controller.enumerate.interval,0)
+# WebVR Tests have been disabled as refactoring of gfxVRPuppet is landing. Dependencies for re-enabling these are tracked by meta bug 1555185.
+# VR SubmitFrame is only implemented for D3D11.1 and MacOSX now.
+# Our Windows 7 test machines don't support D3D11.1, so we run these tests on Windows 8+ only.
+# skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||!layersGPUAccelerated) == draw_rect.html wrapper.html?draw_rect.png
+# On MacOSX platform, getting different color interpolation result.
+# For lower resolution Mac hardware, we need to adjust it to fuzzy-if(cocoaWidget,0-1,0-1200).
+# fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||cocoaWidget,0-1,0-600) skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||!layersGPUAccelerated) == change_size.html wrapper.html?change_size.png
diff --git a/dom/vr/test/reftest/webgl-util.js b/dom/vr/test/reftest/webgl-util.js
new file mode 100644
index 0000000000..42f1c5ccd5
--- /dev/null
+++ b/dom/vr/test/reftest/webgl-util.js
@@ -0,0 +1,61 @@
+WebGLUtil = (function() {
+ // ---------------------------------------------------------------------------
+ // WebGL helpers
+
+ // Returns a valid shader, or null on errors.
+ function createShaderById(gl, id) {
+ var elem = document.getElementById(id);
+ if (!elem) {
+ throw new Error(
+ "Failed to create shader from non-existent id '" + id + "'."
+ );
+ }
+
+ var src = elem.innerHTML.trim();
+
+ var shader;
+ if (elem.type == "x-shader/x-fragment") {
+ shader = gl.createShader(gl.FRAGMENT_SHADER);
+ } else if (elem.type == "x-shader/x-vertex") {
+ shader = gl.createShader(gl.VERTEX_SHADER);
+ } else {
+ throw new Error(
+ "Bad MIME type for shader '" + id + "': " + elem.type + "."
+ );
+ }
+
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+
+ return shader;
+ }
+
+ function createProgramByIds(gl, vsId, fsId) {
+ var vs = createShaderById(gl, vsId);
+ var fs = createShaderById(gl, fsId);
+ if (!vs || !fs) {
+ return null;
+ }
+
+ var prog = gl.createProgram();
+ gl.attachShader(prog, vs);
+ gl.attachShader(prog, fs);
+ gl.linkProgram(prog);
+
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
+ var str = "Shader program linking failed:";
+ str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog);
+ str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs);
+ str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs);
+ console.error(str);
+ return null;
+ }
+
+ return prog;
+ }
+
+ return {
+ createShaderById,
+ createProgramByIds,
+ };
+})();
diff --git a/dom/vr/test/reftest/wrapper.html b/dom/vr/test/reftest/wrapper.html
new file mode 100644
index 0000000000..40d0de6e42
--- /dev/null
+++ b/dom/vr/test/reftest/wrapper.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<title>Image reftest wrapper</title>
+<style type="text/css">
+ #image1 { background-color: rgb(10, 100, 250); }
+</style>
+<script>
+ // The image is loaded async after the page loads
+ // wait for it to finish loading
+ function onImageLoad() {
+ document.documentElement.removeAttribute("class");
+ };
+</script>
+</head>
+<body>
+<img id="image1">
+<script>
+ // Use as "wrapper.html?image.png"
+ var imgURL = document.location.search.substr(1);
+ document.images[0].onload = onImageLoad;
+ document.images[0].onerror = onImageLoad;
+ document.images[0].src = imgURL;
+</script>
+</body>
+</html>