summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/pointerlock
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/tests/mochitest/pointerlock/file_allowPointerLockSandboxFlag.html96
-rw-r--r--dom/tests/mochitest/pointerlock/file_changeLockElement.html115
-rw-r--r--dom/tests/mochitest/pointerlock/file_childIframe.html134
-rw-r--r--dom/tests/mochitest/pointerlock/file_doubleLock.html64
-rw-r--r--dom/tests/mochitest/pointerlock/file_escapeKey.html77
-rw-r--r--dom/tests/mochitest/pointerlock/file_infiniteMovement.html114
-rw-r--r--dom/tests/mochitest/pointerlock/file_locksvgelement.html62
-rw-r--r--dom/tests/mochitest/pointerlock/file_movementXY.html106
-rw-r--r--dom/tests/mochitest/pointerlock/file_nestedFullScreen.html76
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerLockPref.html75
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlock-api-with-shadow.html110
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlock-api.html104
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe.html125
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html95
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_not_focused.html112
-rw-r--r--dom/tests/mochitest/pointerlock/file_pointerlockerror.html41
-rw-r--r--dom/tests/mochitest/pointerlock/file_removedFromDOM.html95
-rw-r--r--dom/tests/mochitest/pointerlock/file_retargetMouseEvents.html212
-rw-r--r--dom/tests/mochitest/pointerlock/file_screenClientXYConst.html143
-rw-r--r--dom/tests/mochitest/pointerlock/file_suppressSomeMouseEvents.html158
-rw-r--r--dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html69
-rw-r--r--dom/tests/mochitest/pointerlock/file_withoutDOM.html54
-rw-r--r--dom/tests/mochitest/pointerlock/iframe_differentDOM.html7
-rw-r--r--dom/tests/mochitest/pointerlock/mochitest.ini37
-rw-r--r--dom/tests/mochitest/pointerlock/pointerlock_utils.js99
-rw-r--r--dom/tests/mochitest/pointerlock/test_closewindow-with-pointerlock.html51
-rw-r--r--dom/tests/mochitest/pointerlock/test_pointerlock-api.html113
-rw-r--r--dom/tests/mochitest/pointerlock/test_pointerlock_focus.html111
-rw-r--r--dom/tests/mochitest/pointerlock/test_pointerlock_target_not_in_active_document.html77
-rw-r--r--dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html59
30 files changed, 2791 insertions, 0 deletions
diff --git a/dom/tests/mochitest/pointerlock/file_allowPointerLockSandboxFlag.html b/dom/tests/mochitest/pointerlock/file_allowPointerLockSandboxFlag.html
new file mode 100644
index 0000000000..33f550f7d7
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_allowPointerLockSandboxFlag.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=784402
+ -->
+ <head>
+ <title>Bug 784402</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=784402">
+ Mozilla Bug 784402</a>
+ <p id="display"></p>
+
+ <iframe src ="iframe_differentDOM.html" allowfullscreen="true" id="iframe"
+ onload="startTest()"
+ sandbox="allow-scripts allow-same-origin allow-pointer-lock">
+ </iframe>
+
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 784402
+ * Test allow-pointer-lock sandbox flag.
+ */
+
+ SimpleTest.waitForExplicitFinish(1);
+
+ var iframe = document.getElementById("iframe")
+ , iframeDiv
+ , contentDocument
+ , pointerLocked = 0
+ , numberOfRuns = 0;
+
+ function runTests () {
+ is(pointerLocked, 1, "Pointer should only have been locked once. " +
+ "Without allow-pointer-lock flag, a sandboxed document should not be " +
+ "able to lock the pointer");
+ SimpleTest.finish();
+ }
+
+ function resetIframe () {
+ contentDocument.exitFullscreen();
+
+ // remove allow-pointer-lock sandbox flag
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
+ // reloads the iframe, startTest function gets called again
+ iframe.setAttribute("src", "iframe_differentDOM.html");
+ }
+
+ function startTest () {
+ SimpleTest.waitForFocus(doStartTest, iframe.contentWindow);
+ }
+ function doStartTest() {
+ contentDocument = iframe.contentDocument;
+ iframeDiv = contentDocument.getElementById("div");
+
+ numberOfRuns++;
+
+ contentDocument.addEventListener("pointerlockchange", function () {
+ if (contentDocument.pointerLockElement === iframeDiv) {
+ pointerLocked++;
+ contentDocument.exitFullscreen();
+ }
+ });
+
+ contentDocument.addEventListener("pointerlockerror", function () {
+ contentDocument.exitFullscreen();
+ });
+
+ contentDocument.addEventListener("fullscreenchange", function () {
+ if (contentDocument.fullscreenElement) {
+ ok(contentDocument.fullscreenElement === iframeDiv,
+ "Fullscreen element can only be iframe div");
+ // during second run iframe won't have allow-pointer-lock flag and
+ // requestPointerLock will fail, pointerlockerror should be fired
+ iframeDiv.requestPointerLock();
+ } else if (numberOfRuns === 1) {
+ resetIframe();
+ } else if (numberOfRuns === 2) {
+ runTests();
+ }
+ });
+
+ iframeDiv.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_changeLockElement.html b/dom/tests/mochitest/pointerlock/file_changeLockElement.html
new file mode 100644
index 0000000000..62c5b27956
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_changeLockElement.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1284788</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #block1, #block2, #block3 {
+ background: blue;
+ width: 50px; height: 50px;
+ margin: 10px;
+ }
+ </style>
+</head>
+<body>
+ <div id="block1"></div>
+ <div id="block2"></div>
+ <div id="block3"></div>
+ <div id="test">
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("For changing pointer lock element not in a valid user event handler");
+
+ var block1 = document.getElementById("block1");
+ var block2 = document.getElementById("block2");
+ var block3 = document.getElementById("block3");
+
+ class ClickTester {
+ constructor(target) {
+ this._target = target;
+ this._callback = null;
+ document.addEventListener("click", this);
+ }
+
+ synthesize(callback) {
+ ok(!this._callback, "No callback should have been hooked");
+ this._callback = callback;
+ synthesizeMouseAtCenter(this._target, {}, window);
+ }
+
+ handleEvent(e) {
+ ok(!!this._callback, "Should have hooked a callback");
+ var callback = this._callback;
+ this._callback = null;
+ callback(e);
+ }
+ };
+
+ var tester = new ClickTester(block3);
+ // It would be called in handler of load event in pointerlock_utils.js
+ function start() {
+ tester.synthesize(firstClick);
+ }
+
+ function firstClick(e) {
+ is(e.target, block3, "Click is triggered inside block3");
+ document.addEventListener("pointerlockchange", lockedPointerOnBlock1);
+ block1.requestPointerLock();
+ }
+
+ function lockedPointerOnBlock1() {
+ document.removeEventListener("pointerlockchange", lockedPointerOnBlock1);
+ is(document.pointerLockElement, block1, "Pointer should be locked on #block1");
+ SimpleTest.executeSoon(() => {
+ tester.synthesize(secondClick);
+ });
+ }
+
+ function secondClick(e) {
+ is(e.target, block1, "Event should be redirected to block1");
+ // Use 2s to ensure that we never consider this as an extension of user input.
+ setTimeout(() => {
+ document.addEventListener("pointerlockchange", lockedPointerOnBlock2);
+ block2.requestPointerLock();
+ }, 2000);
+ }
+
+ function lockedPointerOnBlock2() {
+ document.removeEventListener("pointerlockchange", lockedPointerOnBlock2);
+ is(document.pointerLockElement, block2, "Pointer should be locked on #block2");
+ SimpleTest.executeSoon(() => {
+ tester.synthesize(thirdClick);
+ });
+ }
+
+ function thirdClick(e) {
+ is(e.target, block2, "Event should be redirected to block2");
+ // Use 2s to ensure that we never consider this as an extension of user input.
+ setTimeout(() => {
+ document.addEventListener("pointerlockchange", lockedPointerOnBlock1Again);
+ block1.requestPointerLock();
+ }, 2000);
+ }
+
+ function lockedPointerOnBlock1Again() {
+ document.removeEventListener("pointerlockchange", lockedPointerOnBlock1Again);
+ is(document.pointerLockElement, block1, "Pointer should be locked on #block1 again");
+ SimpleTest.executeSoon(() => {
+ tester.synthesize(fourthClick);
+ });
+ }
+
+ function fourthClick(e) {
+ is(e.target, block1, "Event should be redirected to block1 again");
+ document.addEventListener("pointerlockchange", () => SimpleTest.finish());
+ document.exitPointerLock();
+ }
+
+ </script>
+ </div>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_childIframe.html b/dom/tests/mochitest/pointerlock/file_childIframe.html
new file mode 100644
index 0000000000..9ee3153d2e
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_childIframe.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+<head>
+ <title>Bug 633602 - file_childIframe.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #parent, #childDiv, #iframe, #table, #table td {
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+ #iframe, #table {
+ background-color: red;
+ width: 100%;
+ height: 100%;
+ }
+ #childDiv, #table td {
+ background-color: blue;
+ width: 50%;
+ height: 50%;
+ }
+ </style>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+
+ <div id="parent">
+ <table id="childTable">
+ <tr>
+ <td>
+ <iframe id="iframe" src="iframe_differentDOM.html">
+ </iframe>
+ </td>
+ <td>
+ <div id="childDiv">
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Check if pointer is locked when over a child iframe of
+ * the locked element
+ * Check if pointer is being repositioned back to center of
+ * the locked element even when pointer goes over a child ifame
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var parent = document.getElementById("parent")
+ , childDiv = document.getElementById("childDiv")
+ , iframe = document.getElementById("iframe");
+
+ function MovementStats() {
+ this.movementX = false;
+ this.movementY = false;
+ }
+
+ var firstMove = new MovementStats()
+ , secondMove = new MovementStats()
+ , hoverIframe = false;
+
+ function runTests () {
+ ok(hoverIframe, "Pointer should be locked even when pointer " +
+ "hovers over a child iframe");
+ is(firstMove.movementX, secondMove.movementX, "MovementX of first " +
+ "move to childDiv should be equal to movementX of second move " +
+ "to child div");
+ is(firstMove.movementY, secondMove.movementY, "MovementY of first " +
+ "move to childDiv should be equal to movementY of second move " +
+ "to child div");
+ }
+
+ var firstMoveChild = function (e) {
+ firstMove.movementX = e.movementX;
+ firstMove.movementY = e.movementY;
+
+ parent.removeEventListener("mousemove", firstMoveChild);
+ parent.addEventListener("mousemove", moveIframe);
+
+ synthesizeMouseAtCenter(iframe, {type: "mousemove"}, window);
+ };
+
+ var moveIframe = function (e) {
+ hoverIframe = !!document.pointerLockElement;
+
+ parent.removeEventListener("mousemove", moveIframe);
+ parent.addEventListener("mousemove", secondMoveChild);
+
+ synthesizeMouseAtCenter(childDiv, {type: "mousemove"}, window);
+ };
+
+ var secondMoveChild = function (e) {
+ secondMove.movementX = e.movementX;
+ secondMove.movementY = e.movementY;
+ parent.removeEventListener("mousemove", secondMoveChild);
+
+ addFullscreenChangeContinuation("exit", function() {
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ };
+
+ document.addEventListener("pointerlockchange", function () {
+ if (document.pointerLockElement === parent) {
+ parent.addEventListener("mousemove", firstMoveChild);
+ synthesizeMouseAtCenter(childDiv, {type: "mousemove"}, window);
+ }
+ });
+
+ function start() {
+ addFullscreenChangeContinuation("enter", function() {
+ parent.requestPointerLock();
+ });
+ parent.requestFullscreen();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_doubleLock.html b/dom/tests/mochitest/pointerlock/file_doubleLock.html
new file mode 100644
index 0000000000..93e156a0b0
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_doubleLock.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+<head>
+ <title>Bug 633602 - file_doubleLockCallBack.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #test-element { background-color: #94E01B; width:100px; height:100px; }
+ </style>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602</a>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 633602
+ * If element requests pointerlock on itself while in pointerlock state
+ * pointerlockchange event should be dispatched
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.getElementById("div")
+ , numberOfLocks = 0;
+
+ function runTests () {
+ is(numberOfLocks, 2, "Requesting pointer lock on a locked element " +
+ "should dispatch pointerlockchange event");
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === div) {
+ if (numberOfLocks === 2) {
+ addFullscreenChangeContinuation("exit", function() {
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ else {
+ numberOfLocks++;
+ div.requestPointerLock();
+ }
+ }
+ });
+
+ function start() {
+ console.log('started');
+ addFullscreenChangeContinuation("enter", function() {
+ div.requestPointerLock();
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_escapeKey.html b/dom/tests/mochitest/pointerlock/file_escapeKey.html
new file mode 100644
index 0000000000..987b880232
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_escapeKey.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
+<head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 633602
+ * Escape key should unlock the pointer
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.getElementById("div")
+ , pointerUnLocked = false;
+
+ function start() {
+ addFullscreenChangeContinuation("enter", enteredFullscreen);
+ div.requestFullscreen();
+ }
+
+ function enteredFullscreen(e) {
+ is(document.fullscreenElement, div, "Element #div should entered fullscreen");
+ ok(!document.pointerLockElement, "Pointer shouldn't have been locked");
+ document.addEventListener("pointerlockchange", lockedPointer);
+ div.requestPointerLock();
+ }
+
+ function lockedPointer(e) {
+ document.removeEventListener("pointerlockchange", lockedPointer);
+ is(document.pointerLockElement, div, "Pointer should have been locked on #div");
+ document.addEventListener("pointerlockchange", unlockedPointer);
+ addFullscreenChangeContinuation("exit", leavedFullscreen);
+ SimpleTest.executeSoon(() => synthesizeKey("KEY_Escape"));
+ }
+
+ var pointerUnlocked = false;
+ var exitedFullscreen = false;
+
+ function unlockedPointer() {
+ document.removeEventListener("pointerlockchange", unlockedPointer);
+ ok(!pointerUnlocked, "Shouldn't have unlocked pointer before");
+ ok(!document.pointerLockElement, "Pointer should have been unlocked now");
+ pointerUnlocked = true;
+ finishTest();
+ }
+
+ function leavedFullscreen() {
+ ok(!exitedFullscreen, "Shouldn't have exited fullscreen before");
+ ok(!document.fullscreenElement, "Should have exited fullscreen now");
+ exitedFullscreen = true;
+ finishTest();
+ }
+
+ function finishTest() {
+ if (pointerUnlocked && exitedFullscreen) {
+ SimpleTest.finish();
+ }
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_infiniteMovement.html b/dom/tests/mochitest/pointerlock/file_infiniteMovement.html
new file mode 100644
index 0000000000..995d99843b
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_infiniteMovement.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+ <head>
+ <title>Bug 633602 - file_movementXY.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * This test checks if movementX and movementY
+ * are present in a mouse event object.
+ * It also checks the values for movementXY.
+ * They should be equal to the current screenXY minus
+ * the last screenXY
+ * This test will also test that the incremental movement is
+ * not constrained to the width of the screen.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.getElementById("div")
+ , divCenterWidth = 0
+ , divCenterHeight = 0
+ , totalMovementX = 0
+ , totalMovementY = 0
+ , mouseMoveIntervalID;
+
+ function runTests () {
+ ok(totalMovementX > div.getBoundingClientRect().width,
+ "Should have moved more than one screen's worth in width." +
+ "TotalX: " + totalMovementX + " Screensize X: " + div.getBoundingClientRect().width);
+ ok(totalMovementY > div.getBoundingClientRect().height,
+ "Should have moved more than one screen's worth in height." +
+ "TotalY: " + totalMovementY + " Screensize Y: " + div.getBoundingClientRect().height);
+ }
+
+ var firstMoveListener = function (e) {
+ info("Got first mousemove");
+ clearInterval(mouseMoveIntervalID);
+ div.removeEventListener("mousemove", firstMoveListener);
+ div.addEventListener("mousemove", secondMoveListener);
+
+ // Bug 1357082
+ // Retrigger synthesizeMouse until it actually happens.
+ mouseMoveIntervalID = setInterval(() => {
+ synthesizeMouse(div,(divCenterWidth/2) * 3,
+ (divCenterHeight/2) * 3, {
+ type: "mousemove"
+ }, window);
+ }, 100);
+ }
+
+ var secondMoveListener = function (e) {
+ info("Got second mousemove");
+ clearInterval(mouseMoveIntervalID);
+ totalMovementX = divCenterWidth + ((divCenterWidth / 2) * 3);
+ totalMovementY = divCenterHeight + ((divCenterHeight / 2) * 3);
+
+ div.removeEventListener("mousemove", secondMoveListener);
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === div) {
+ info("Got pointerlockchange for entering");
+ div.addEventListener("mousemove", firstMoveListener);
+
+ divCenterWidth = Math.round(div.getBoundingClientRect().width / 2);
+ divCenterHeight = Math.round(div.getBoundingClientRect().height / 2);
+
+ // Bug 1357082
+ // Retrigger synthesizeMouse until it actually happens.
+ mouseMoveIntervalID = setInterval(() => {
+ synthesizeMouse(div, divCenterWidth, divCenterHeight, {
+ type: "mousemove"
+ }, window);
+ }, 100);
+ } else {
+ info("Got pointerlockchange for exiting");
+ }
+ });
+
+ function start() {
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", function() {
+ info("Got fullscreenchange for entering");
+ div.requestPointerLock();
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_locksvgelement.html b/dom/tests/mochitest/pointerlock/file_locksvgelement.html
new file mode 100644
index 0000000000..edbfa45995
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_locksvgelement.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+ -->
+ <head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602</a>
+ <p id="display"></p>
+
+ <svg id="svg-elem" width="100" height="100" viewbox="0 0 100 100">
+ <rect x="10" y="10" width="50" height="50"
+ fill="black" stroke="blue" stroke-width="2"/>
+ </svg>
+
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Test locking non-html element.
+ */
+
+ SimpleTest.waitForExplicitFinish(1);
+
+ var elem,
+ elemWasLocked = false;
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.fullscreenElement &&
+ document.pointerLockElement === elem) {
+ elemWasLocked = true;
+ document.exitPointerLock();
+ } else {
+ addFullscreenChangeContinuation("exit", function() {
+ ok(elemWasLocked, "Expected SVG elem to become locked.");
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ });
+
+ function start() {
+ elem = document.getElementById("svg-elem");
+ addFullscreenChangeContinuation("enter", function() {
+ elem.requestPointerLock();
+ });
+ elem.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_movementXY.html b/dom/tests/mochitest/pointerlock/file_movementXY.html
new file mode 100644
index 0000000000..7ef47cfaa9
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_movementXY.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+ <head>
+ <title>Bug 633602 - file_movementXY.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Checks if movementX and movementY are present
+ * in the mouse event object.
+ * It also checks the values for movementXY.
+ * They should be equal to the current screenXY minus
+ * the last screenXY
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("We may need to wait for window's moving");
+
+ function MouseMovementStats() {
+ this.screenX = false;
+ this.screenY = false;
+ this.movementX = false;
+ this.movementY = false;
+ }
+
+ var div = document.getElementById("div")
+ , divCenterWidth = 0
+ , divCenterHeight = 0
+ , movementX = false
+ , movementY = false
+ , firstMove = new MouseMovementStats()
+ , secondMove = new MouseMovementStats();
+
+ function runTests () {
+ ok(movementX && movementY, "movementX and " +
+ "movementY should exist in mouse events objects.");
+ is(secondMove.movementX, secondMove.screenX - firstMove.screenX,
+ "movementX should be equal to eNow.screenX-ePrevious.screenX");
+ is(secondMove.movementY, secondMove.screenY - firstMove.screenY,
+ "movementY should be equal to eNow.screenY-ePrevious.screenY");
+ }
+
+ var moveMouse = function(e) {
+ info("Got mouse move");
+ movementX = ("movementX" in e);
+ movementY = ("movementY" in e);
+
+ div.removeEventListener("mousemove", moveMouse);
+ div.addEventListener("mousemove", moveMouseAgain);
+
+ firstMove.screenX = e.screenX;
+ firstMove.screenY = e.screenY;
+
+ divCenterWidth = Math.round(div.getBoundingClientRect().width / 2);
+ divCenterHeight = Math.round(div.getBoundingClientRect().height / 2);
+
+ synthesizeMouse(div, (divCenterWidth + 10), (divCenterHeight + 10), {
+ type: "mousemove"
+ }, window);
+ };
+
+ var moveMouseAgain = function(e) {
+ info("Got mouse move again");
+ secondMove.screenX = e.screenX;
+ secondMove.screenY = e.screenY;
+ secondMove.movementX = e.movementX;
+ secondMove.movementY = e.movementY;
+
+ div.removeEventListener("mousemove", moveMouseAgain);
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ };
+
+ function start() {
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", function() {
+ info("Got fullscreenchange for entering");
+ div.addEventListener("mousemove", moveMouse);
+ synthesizeMouseAtCenter(div, {type: "mousemove"}, window);
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_nestedFullScreen.html b/dom/tests/mochitest/pointerlock/file_nestedFullScreen.html
new file mode 100644
index 0000000000..8d625b6704
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_nestedFullScreen.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+ -->
+ <head>
+ <title>Bug 633602 - file_nestedFullScreen.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+
+ <div id="parentDiv">
+ <div id="childDiv"></div>
+ </div>
+
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Requesting fullscreen on a child element of the element with
+ * the pointer locked should unlock the pointer
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var parentDiv = document.getElementById("parentDiv")
+ , childDiv = document.getElementById("childDiv")
+ , parentDivLocked = false
+ , parentDivFullScreen = false
+ , pointerLocked = false;
+
+ function runTests () {
+ ok(parentDivLocked, "After requesting pointerlock on parentDiv " +
+ "document.pointerLockElement should be equal to " +
+ " parentDiv element");
+ isnot(pointerLocked, true, "Requesting fullscreen on " +
+ "childDiv while parentDiv still in fullscreen should " +
+ "unlock the pointer");
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === parentDiv) {
+ parentDivLocked = true;
+ addFullscreenChangeContinuation("enter", function() {
+ pointerLocked = !!document.pointerLockElement;
+ addFullscreenChangeContinuation("exit", function() {
+ addFullscreenChangeContinuation("exit", function() {
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ });
+ document.exitFullscreen();
+ });
+ childDiv.requestFullscreen();
+ }
+ });
+
+ function start() {
+ addFullscreenChangeContinuation("enter", function() {
+ parentDivFullScreen = true;
+ parentDiv.requestPointerLock();
+ });
+ parentDiv.requestFullscreen();
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerLockPref.html b/dom/tests/mochitest/pointerlock/file_pointerLockPref.html
new file mode 100644
index 0000000000..09c3b42052
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerLockPref.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+ -->
+ <head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602</a>
+ <p id="display"></p>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Tests full-screen-api.pointer-lock pref
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.getElementById("div")
+ , prefEnabled = false
+ , prefDisabled = false;
+
+ function runTests () {
+ ok(prefEnabled, "Element should be able to lock the pointer " +
+ "if pointer-lock pref is set to TRUE");
+ ok(prefDisabled, "Element should NOT be able to lock the pointer " +
+ "if pointer-lock pref is set to FALSE");
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === div) {
+ prefEnabled = true;
+ document.exitPointerLock();
+ }
+ else {
+ SpecialPowers.setBoolPref("full-screen-api.pointer-lock.enabled",
+ false );
+ div.requestPointerLock();
+ }
+ });
+
+ document.addEventListener("pointerlockerror", function (e) {
+ prefDisabled = true;
+ addFullscreenChangeContinuation("exit", function() {
+ SpecialPowers.setBoolPref("full-screen-api.pointer-lock.enabled",
+ true );
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ });
+
+ function start() {
+ addFullscreenChangeContinuation("enter", function() {
+ SpecialPowers.setBoolPref("full-screen-api.pointer-lock.enabled",
+ true );
+ div.requestPointerLock();
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock-api-with-shadow.html b/dom/tests/mochitest/pointerlock/file_pointerlock-api-with-shadow.html
new file mode 100644
index 0000000000..a9c2d525de
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlock-api-with-shadow.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
+<head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="host"></div>
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 1430303
+ * Make sure DOM API is correct.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div,
+ host,
+ hasRequestPointerLock = false,
+ pointerLockChangeEventFired = false,
+ pointerUnlocked = false,
+ pointerLocked = false,
+ hasExitPointerLock = false,
+ pointerLockElement = false,
+ hasMovementX = false,
+ hasMovementY = false;
+ gotContextMenuEvent = false;
+
+ function runTests () {
+ ok(hasRequestPointerLock, "Element should have requestPointerLock.");
+ ok(pointerLockChangeEventFired, "pointerlockchange event should fire.");
+ ok(pointerUnlocked, "Should be able to unlock pointer locked element.");
+ ok(pointerLocked, "Requested element should be able to lock.");
+ ok(hasExitPointerLock, "Document should have exitPointerLock");
+ ok(pointerLockElement, "Document should keep track of correct pointer locked element");
+ ok(hasMovementX, "Mouse Event should have movementX.");
+ ok(hasMovementY, "Mouse Event should have movementY.");
+ ok(!gotContextMenuEvent, "Shouldn't have got a contextmenu event.");
+ }
+
+ function mouseMoveHandler(e) {
+ info("Got mousemove");
+ document.removeEventListener("mousemove", mouseMoveHandler);
+
+ hasMovementX = "movementX" in e;
+ hasMovementY = "movementY" in e;
+
+ hasExitPointerLock = "exitPointerLock" in document;
+ document.exitPointerLock();
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ pointerLockChangeEventFired = true;
+
+ if (document.pointerLockElement) {
+ info("Got pointerlockchange for entering");
+ pointerLocked = true;
+ pointerLockElement = document.pointerLockElement === host;
+ is(host.shadowRoot.firstChild, host.shadowRoot.pointerLockElement,
+ "ShadowRoot's pointerLockElement shouldn't be retargeted.");
+
+ window.addEventListener("contextmenu",
+ function() { gotContextMenuEvent = true; },
+ true);
+ synthesizeMouse(document.body, 4, 4,
+ { type: "contextmenu", button: 2 },
+ window);
+
+ document.addEventListener("mousemove", mouseMoveHandler);
+ synthesizeMouseAtCenter(host, {type: "mousemove"}, window);
+ } else {
+ info("Got pointerlockchange for exiting");
+ pointerUnlocked = true;
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ });
+
+ function start() {
+ host = document.getElementById("host");
+ var sr = host.attachShadow({mode: "open"});
+ sr.innerHTML = "<div><div>";
+ div = sr.firstChild;
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", function() {
+ info("Got fullscreenchange for entering");
+ hasRequestPointerLock = "requestPointerLock" in div;
+ div.requestPointerLock();
+ });
+ host.requestFullscreen();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock-api.html b/dom/tests/mochitest/pointerlock/file_pointerlock-api.html
new file mode 100644
index 0000000000..6f5fb537be
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlock-api.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
+<head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="div"></div>
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 633602
+ * Make sure DOM API is correct.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div,
+ hasRequestPointerLock = false,
+ pointerLockChangeEventFired = false,
+ pointerUnlocked = false,
+ pointerLocked = false,
+ hasExitPointerLock = false,
+ pointerLockElement = false,
+ hasMovementX = false,
+ hasMovementY = false;
+ gotContextMenuEvent = false;
+
+ function runTests () {
+ ok(hasRequestPointerLock, "Element should have requestPointerLock.");
+ ok(pointerLockChangeEventFired, "pointerlockchange event should fire.");
+ ok(pointerUnlocked, "Should be able to unlock pointer locked element.");
+ ok(pointerLocked, "Requested element should be able to lock.");
+ ok(hasExitPointerLock, "Document should have exitPointerLock");
+ ok(pointerLockElement, "Document should keep track of correct pointer locked element");
+ ok(hasMovementX, "Mouse Event should have movementX.");
+ ok(hasMovementY, "Mouse Event should have movementY.");
+ ok(!gotContextMenuEvent, "Shouldn't have got a contextmenu event.");
+ }
+
+ function mouseMoveHandler(e) {
+ info("Got mousemove");
+ document.removeEventListener("mousemove", mouseMoveHandler);
+
+ hasMovementX = "movementX" in e;
+ hasMovementY = "movementY" in e;
+
+ hasExitPointerLock = "exitPointerLock" in document;
+ document.exitPointerLock();
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ pointerLockChangeEventFired = true;
+
+ if (document.pointerLockElement) {
+ info("Got pointerlockchange for entering");
+ pointerLocked = true;
+ pointerLockElement = document.pointerLockElement === div;
+
+ window.addEventListener("contextmenu",
+ function() { gotContextMenuEvent = true; },
+ true);
+ synthesizeMouse(document.body, 4, 4,
+ { type: "contextmenu", button: 2 },
+ window);
+
+ document.addEventListener("mousemove", mouseMoveHandler);
+ synthesizeMouseAtCenter(div, {type: "mousemove"}, window);
+ } else {
+ info("Got pointerlockchange for exiting");
+ pointerUnlocked = true;
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ });
+
+ function start() {
+ div = document.getElementById("div");
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", function() {
+ info("Got fullscreenchange for entering");
+ hasRequestPointerLock = "requestPointerLock" in div;
+ div.requestPointerLock();
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe.html b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe.html
new file mode 100644
index 0000000000..f8e34ef64b
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=1662587-->
+<head>
+<title>Bug 1662587</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="pointerlock_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1662587">Mozilla Bug 1662587</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/tests/mochitest/pointerlock/iframe_differentDOM.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1662587
+ */
+SimpleTest.waitForExplicitFinish();
+
+async function requestPointerLock(aElement, aExpectError = false) {
+ let doc = aElement.ownerDocument;
+ let waitForPointerLockEvent = function() {
+ return new Promise((aResolve) => {
+ let eventHandler = function(aEvent) {
+ is(aEvent.type, aExpectError ? 'pointerlockerror' : 'pointerlockchange',
+ `got ${aEvent.type}`);
+ doc.removeEventListener('pointerlockchange', eventHandler);
+ doc.removeEventListener('pointerlockerror', eventHandler);
+ aResolve();
+ };
+
+ doc.addEventListener('pointerlockchange', eventHandler);
+ doc.addEventListener('pointerlockerror', eventHandler);
+ });
+ };
+
+ SpecialPowers.wrap(doc).notifyUserGestureActivation();
+ aElement.requestPointerLock();
+ await waitForPointerLockEvent();
+ is(doc.pointerLockElement, aExpectError ? null : aElement, "target pointer locked");
+}
+
+async function exitPointerLock() {
+ if (document.pointerLockElement) {
+ document.exitPointerLock();
+ await new Promise((aResolve) => {
+ document.addEventListener("pointerlockchange", (aEvent) => {
+ ok(true, `got ${aEvent.type}`);
+ aResolve();
+ }, { once: true });
+ });
+ }
+ is(document.pointerLockElement, null, "pointer unlocked");
+}
+
+async function start() {
+ let iframe = document.querySelector("iframe");
+ let target = document.getElementById("target");
+ await requestPointerLock(target);
+
+ // Mouse event should be dispatched to locked element.
+ synthesizeMouse(iframe, 10, 10, { type: "mousemove" });
+ await new Promise((aResolve) => {
+ document.addEventListener("mousemove", function(e) {
+ info("Got mousemove");
+ ok("movementX" in e, "has movementX");
+ ok("movementY" in e, "has movementY");
+ is(e.target, target, "event.target");
+ aResolve();
+ }, { once: true });
+ });
+
+ // Mouse wheel event should be dispatched to locked element.
+ synthesizeWheel(iframe, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ await new Promise((aResolve) => {
+ document.addEventListener("wheel", function(e) {
+ info("Got wheel");
+ is(e.target, target, "event.target");
+ aResolve();
+ }, { once: true });
+ });
+
+ // Pointer lock requested in iframe should be rejected.
+ let win = iframe.contentWindow;
+ win.focus()
+ await SpecialPowers.spawn(win, [], async () => {
+ info("request pointer lock in xorigin iframe");
+ content.document.querySelector("input").focus();
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ content.document.body.requestPointerLock();
+ await new Promise((aResolve) => {
+ let handler = function(aEvent) {
+ is(aEvent.type, 'pointerlockerror', `got ${aEvent.type}`);
+ content.document.onpointerlockchange = null;
+ content.document.onpointerlockerror = null;
+ aResolve();
+ };
+ content.document.onpointerlockchange = handler;
+ content.document.onpointerlockerror = handler;
+ });
+ content.document.exitPointerLock();
+ });
+
+ await exitPointerLock();
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html
new file mode 100644
index 0000000000..68977ffada
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=1672330-->
+<head>
+<title>Bug 1672330</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="pointerlock_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1672330">Mozilla Bug 1672330</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/tests/mochitest/pointerlock/iframe_differentDOM.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1672330
+ */
+SimpleTest.waitForExplicitFinish();
+
+async function requestPointerLock(aElement, aExpectError = false) {
+ let doc = aElement.ownerDocument;
+ let waitForPointerLockEvent = function() {
+ return new Promise((aResolve) => {
+ let eventHandler = function(aEvent) {
+ is(aEvent.type, aExpectError ? 'pointerlockerror' : 'pointerlockchange',
+ `got ${aEvent.type}`);
+ doc.removeEventListener('pointerlockchange', eventHandler);
+ doc.removeEventListener('pointerlockerror', eventHandler);
+ aResolve();
+ };
+
+ doc.addEventListener('pointerlockchange', eventHandler);
+ doc.addEventListener('pointerlockerror', eventHandler);
+ });
+ };
+
+ aElement.requestPointerLock();
+ await waitForPointerLockEvent();
+ is(doc.pointerLockElement, aExpectError ? null : aElement, "target pointer locked");
+}
+
+async function start() {
+ await waitUntilApzStable();
+
+ let target = document.getElementById("target");
+ SpecialPowers.wrap(document).clearUserGestureActivation();
+ // Pointer lock request should be rejected due to the lack of user gesture.
+ await requestPointerLock(target, true);
+
+ // Test mouse event should not be dispatched to document.
+ document.addEventListener("mousemove", function(e) {
+ ok(false, "Got unexpected mousemove");
+ });
+
+ info("test sending mouse event to iframe");
+ let iframe = document.querySelector("iframe");
+ synthesizeMouse(iframe, 10, 10, { type: "mousemove" });
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+
+ info("test sending mouse event to another window");
+ await new Promise((aResolve) => {
+ let win = window.open("iframe_differentDOM.html");
+ win.addEventListener("load", async function() {
+ info("win onload");
+ await waitUntilApzStable();
+ synthesizeMouse(win.document.body, 10, 10, { type: "mousemove" }, win);
+ win.close();
+ aResolve();
+ });
+ });
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_not_focused.html b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_not_focused.html
new file mode 100644
index 0000000000..a6c0058657
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_not_focused.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=1662587-->
+<head>
+<title>Bug 1662587</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="pointerlock_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 50px;
+ height: 50px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1698611">Mozilla Bug 1698611</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/tests/mochitest/pointerlock/iframe_differentDOM.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1698611
+ */
+SimpleTest.waitForExplicitFinish();
+
+async function requestPointerLock(aWin) {
+ await SpecialPowers.spawn(aWin, [], async () => {
+ info("request pointer lock in xorigin iframe");
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ content.document.body.requestPointerLock();
+ await new Promise((aResolve) => {
+ let handler = function(aEvent) {
+ is(aEvent.type, 'pointerlockchange', `got ${aEvent.type}`);
+ content.document.onpointerlockchange = null;
+ content.document.onpointerlockerror = null;
+ aResolve();
+ };
+ content.document.onpointerlockchange = handler;
+ content.document.onpointerlockerror = handler;
+ });
+ });
+}
+
+async function exitPointerLock(aWin) {
+ await SpecialPowers.spawn(aWin, [], async () => {
+ info("exit pointer lock in xorigin iframe");
+ if (content.document.pointerLockElement) {
+ content.document.exitPointerLock();
+ await new Promise((aResolve) => {
+ content.document.addEventListener("pointerlockchange", (aEvent) => {
+ ok(true, `got ${aEvent.type}`);
+ aResolve();
+ }, { once: true });
+ });
+ }
+ is(content.document.pointerLockElement, null, "pointer unlocked");
+ });
+}
+
+function addEventListenerOnRemote(aWin, aEvent) {
+ return SpecialPowers.spawn(aWin, [aEvent], async (aEvent) => {
+ info(`wait for ${aEvent} event on remote`);
+ content.document.addEventListener(aEvent, function(e) {
+ info(`get ${aEvent} event on remote`);
+ content.parent.postMessage(aEvent, "*");
+ });
+ });
+}
+
+function waitForMessage(aWin, aMessage) {
+ return new Promise((aResolve) => {
+ info(`wait for ${aMessage} message`);
+ window.addEventListener("message", function handler(e) {
+ info(`get ${e.data} message`);
+ if (e.data === aMessage) {
+ window.removeEventListener("message", handler);
+ aResolve();
+ }
+ });
+ });
+}
+
+async function start() {
+ info("Put the focus on top-level document");
+ await SimpleTest.promiseFocus(window);
+
+ let iframe = document.querySelector("iframe");
+ let win = iframe.contentWindow;
+ await requestPointerLock(win);
+ await addEventListenerOnRemote(win, "mousemove");
+
+ let promise = waitForMessage(win, "mousemove");
+ let div = document.querySelector("div");
+ synthesizeMouseAtCenter(div, { type: "mousemove" });
+ await promise;
+
+ await exitPointerLock(win);
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_pointerlockerror.html b/dom/tests/mochitest/pointerlock/file_pointerlockerror.html
new file mode 100644
index 0000000000..0948a19452
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_pointerlockerror.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
+<head>
+ <title>Bug 633602</title>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 633602
+ * Make sure pointerlockerror event fires.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("pointerlockerror", function (e) {
+ ok(true, "pointerlockerror event should fire.");
+ SimpleTest.finish();
+ });
+
+ function start() {
+ // element not in the DOM, not fullscreen, should fail to lock
+ div = document.createElement("div");
+ div.requestPointerLock();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_removedFromDOM.html b/dom/tests/mochitest/pointerlock/file_removedFromDOM.html
new file mode 100644
index 0000000000..3e2f9ca866
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_removedFromDOM.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+
+ Test DOM tree in full screen
+ -->
+<head>
+<title>Bug 633602 - file_DOMtree.html</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="pointerlock_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+</a>
+<div id="div"></div>
+<div id="outer"><div id="inner"></div></div>
+<pre id="test">
+<script type="text/javascript">
+/*
+ * Test for Bug 633602
+ * Checks if pointer is unlocked when element is removed from
+ * the DOM Tree
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var div = document.getElementById("div");
+var outer = document.getElementById("outer");
+var inner = document.getElementById("inner");
+
+function listenOneDocEvent(type, handler) {
+ function callback(event) {
+ document.removeEventListener(type, callback);
+ SimpleTest.executeSoon(() => handler(event));
+ }
+ document.addEventListener(type, callback);
+}
+
+function checkPointerLockElement(elem) {
+ if (elem) {
+ is(document.pointerLockElement, elem,
+ `#${elem.id} should have locked the pointer`);
+ } else {
+ ok(!document.pointerLockElement, "Pointer should have been unlocked");
+ }
+}
+
+function start() {
+ addFullscreenChangeContinuation("enter", enteredFullscreen);
+ document.documentElement.requestFullscreen();
+}
+
+function enteredFullscreen() {
+ is(document.fullscreenElement, document.documentElement,
+ "Root element should have entered fullscreen");
+ listenOneDocEvent("pointerlockchange", lockedPointerOnDiv);
+ div.requestPointerLock();
+}
+
+function lockedPointerOnDiv() {
+ checkPointerLockElement(div);
+ listenOneDocEvent("pointerlockchange", unlockedPointerFromDiv);
+ document.body.removeChild(div);
+}
+
+function unlockedPointerFromDiv() {
+ checkPointerLockElement(null);
+ listenOneDocEvent("pointerlockchange", lockedPointerOnInner);
+ inner.requestPointerLock();
+}
+
+function lockedPointerOnInner() {
+ checkPointerLockElement(inner);
+ listenOneDocEvent("pointerlockchange", unlockedPointerFromInner);
+ document.body.removeChild(outer);
+}
+
+function unlockedPointerFromInner() {
+ checkPointerLockElement(null);
+ addFullscreenChangeContinuation("exit", exitedFullscreen);
+ document.exitFullscreen();
+}
+
+function exitedFullscreen() {
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_retargetMouseEvents.html b/dom/tests/mochitest/pointerlock/file_retargetMouseEvents.html
new file mode 100644
index 0000000000..2cd329bb33
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_retargetMouseEvents.html
@@ -0,0 +1,212 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+<head>
+ <title>Bug 633602 - file_retargetMouseEvents.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+
+ <div id="parent">
+ <div id="child" style="width: 100%; height: 100%;">
+ </div>
+ </div>
+
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Retarget mouse events to the locked element
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ function MouseEventStats() {
+ this.mouseMove = false;
+ this.mouseDown = false;
+ this.mouseUp = false;
+ this.mouseClick = false;
+ this.mouseScroll = false;
+ this.wheel = false;
+ }
+
+ var parent = document.getElementById("parent")
+ , child = document.getElementById("child")
+ , parentStats = new MouseEventStats()
+ , childStats = new MouseEventStats()
+ , mouseMoveIntervalID;
+
+ function runTests () {
+ is(childStats.mouseMove, false, "Child shound not receive mousemove event.");
+ is(childStats.mouseDown, false, "Child should not receive mousedown event.");
+ is(childStats.mouseUp, false, "Child should not receive mouseup event.");
+ is(childStats.mouseClick, false, "Child should not receive click event.");
+ is(childStats.mouseScroll, false, "Child should not receive DOMMouseScroll event.");
+ is(childStats.wheel, false, "Child should not receive wheel event.");
+
+ ok(parentStats.mouseMove, "Parent should receive mousemove event.");
+ ok(parentStats.mouseDown, "Parent should receive mousedown event.");
+ ok(parentStats.mouseUp, "Parent should receive mouseup event.");
+ ok(parentStats.mouseClick, "Parent should receive click event.");
+ ok(parentStats.mouseScroll, "Parent should receive DOMMouseScroll event.");
+ ok(parentStats.wheel, "Parent should receive wheel event.");
+ }
+
+
+ /**
+ * The event listeners for the child element shouldn't be fired
+ * Mouse events will only happen when the pointer is locked
+ * and if the pointer is locked all the mouse events should be
+ * retargetted to the locked element
+ **/
+ var childMoveTest = function() {
+ clearInterval(mouseMoveIntervalID);
+ childStats.mouseMove = true;
+ }
+
+ var childDownTest = function() {
+ childStats.mouseDown = true;
+ };
+
+ var childUpTest = function() {
+ childStats.mouseUp = true;
+ };
+
+ var childClickTest = function() {
+ childStats.mouseClick = true;
+ };
+
+ var childScrollTest = function() {
+ childStats.mouseScroll = true;
+ };
+
+ var childWheelTest = function() {
+ childStats.wheel = true;
+ };
+
+ // Event listeners for the parent element
+ var startMouseTests = function() {
+ info("Got parent mousemove");
+ clearInterval(mouseMoveIntervalID);
+ parent.removeEventListener("mousemove", startMouseTests);
+ parent.addEventListener("DOMMouseScroll", parentScrollTest);
+ child.addEventListener("DOMMouseScroll", childScrollTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeWheel(child, 5, 5, {'deltaY': 10, 'lineOrPageDeltaY': 10,
+ 'deltaMode': WheelEvent.DOM_DELTA_LINE});
+ });
+ };
+
+ var parentScrollTest = function (e) {
+ info("Got parent DOMMouseScroll");
+ parentStats.mouseScroll = true;
+ parent.removeEventListener("DOMMouseScroll", parentScrollTest);
+ child.removeEventListener("DOMMouseScroll", childScrollTest);
+ parent.addEventListener("wheel", parentWheelTest);
+ child.addEventListener("wheel", childWheelTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeWheel(child, 5, 5, {'deltaY': 10, 'lineOrPageDeltaY': 10,
+ 'deltaMode': WheelEvent.DOM_DELTA_LINE});
+ });
+ };
+
+ var parentWheelTest = function (e) {
+ info("Got parent wheel");
+ parentStats.wheel = true;
+ parent.removeEventListener("wheel", parentWheelTest);
+ child.removeEventListener("wheel", childWheelTest);
+ parent.addEventListener("mousedown", parentDownTest);
+ child.addEventListener("mousedown", childDownTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(child, {type: "mousedown"}, window);
+ });
+ };
+
+ var parentDownTest = function (e) {
+ info("Got parent mousedown");
+ parentStats.mouseDown = true;
+ parent.removeEventListener("mousedown", parentDownTest);
+ child.removeEventListener("mousedown", childDownTest);
+ parent.addEventListener("mouseup", parentUpTest);
+ child.addEventListener("mouseup", childUpTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(child, {type: "mouseup"}, window);
+ });
+ };
+
+ var parentUpTest = function (e) {
+ info("Got parent mouseup");
+ parentStats.mouseUp = true;
+ parent.removeEventListener("mouseup", parentUpTest);
+ child.removeEventListener("mouseup", childUpTest);
+ parent.addEventListener("click", parentClickTest);
+ child.addEventListener("click", childClickTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(child, {}, window);
+ });
+ };
+
+ var parentClickTest = function (e) {
+ info("Got parent click");
+ parentStats.mouseClick = true;
+ parent.removeEventListener("click", parentClickTest);
+ child.removeEventListener("click", childClickTest);
+ parent.addEventListener("mousemove", parentMoveTest);
+ child.addEventListener("mousemove", childMoveTest);
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(child, {type: "mousemove"}, window);
+ });
+ };
+
+ var parentMoveTest = function (e) {
+ info("Got parent mousemove");
+ parentStats.mouseMove = true;
+ parent.removeEventListener("mousemove", parentMoveTest);
+ child.removeEventListener("mousemove", childMoveTest);
+ SimpleTest.executeSoon(function () {
+ info("Exit fullscreen");
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ });
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === parent) {
+ info("Got pointerlockchange for entering");
+ parent.addEventListener("mousemove", startMouseTests);
+ child.addEventListener("mousemove", childMoveTest);
+ // Bug 1357082
+ // Retrigger synthesizeMouseAtCenter until it actually happens.
+ mouseMoveIntervalID = setInterval(() => {
+ synthesizeMouseAtCenter(parent, {type: "mousemove"}, window);
+ }, 100);
+ } else {
+ info("Got pointerlockchange for exiting");
+ }
+ });
+
+ function start() {
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", function() {
+ info("Got fullscreenchange for entering");
+ parent.requestPointerLock();
+ });
+ parent.requestFullscreen();
+ }
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html b/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html
new file mode 100644
index 0000000000..31bd232a57
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_screenClientXYConst.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+<head>
+ <title>Bug 633602 - constantXY.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="div"></div>
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Confirm that screenX/Y and clientX/Y are constants when the pointer
+ * is locked.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("We may need to wait for window's moving");
+
+ var div
+ , divRect
+ , unLockedCoords
+ , lockedCoords
+ , mouseMoveIntervalID
+ , isUnlocked = false
+ , isLocked = false;
+
+ function runTests () {
+ ok(isUnlocked, "Pointer should be unlocked");
+ ok(isLocked, "Pointer should be locked");
+
+ // Confirm that pointer coords are constant while locked
+ is(unLockedCoords.clientX, lockedCoords.clientX,
+ "clientX should be equal to where the mouse was originaly locked");
+ is(unLockedCoords.clientY, lockedCoords.clientY,
+ "clientY should be equal to where the mouse was originaly locked");
+ is(unLockedCoords.screenX, lockedCoords.screenX,
+ "screenX should be equal to where the mouse was originaly locked");
+ is(unLockedCoords.screenY, lockedCoords.screenY,
+ "screenY should be equal to where the mouse was originaly locked");
+ }
+
+ function moveUnlocked(e) {
+ info("Got mousemove via moveUnlocked");
+ clearInterval(mouseMoveIntervalID);
+ var firstCall = !unLockedCoords;
+ if (!firstCall) {
+ todo(false, "mousemove is fired twice.");
+ }
+
+ unLockedCoords = {
+ screenX: e.screenX,
+ screenY: e.screenY,
+ clientX: e.clientX,
+ clientY: e.clientY
+ };
+
+ if (!firstCall) {
+ return;
+ }
+
+ isUnlocked = !document.pointerLockElement;
+ div.requestPointerLock();
+ }
+
+ function moveLocked(e) {
+ info("Got mousemove via moveLocked");
+ clearInterval(mouseMoveIntervalID);
+ div.removeEventListener("mousemove", moveLocked);
+
+ isLocked = !!document.pointerLockElement;
+ lockedCoords = {
+ screenX: e.screenX,
+ screenY: e.screenY,
+ clientX: e.clientX,
+ clientY: e.clientY
+ };
+
+ addFullscreenChangeContinuation("exit", function() {
+ info("Got fullscreenchange for exiting");
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === div) {
+ info("Got pointerlockchange for entering");
+ div.removeEventListener("mousemove", moveUnlocked);
+ div.addEventListener("mousemove", moveLocked);
+ divRect = div.getBoundingClientRect();
+ // Bug 1295815
+ // Retrigger synthesizeNativeMouseEvent until it actually happens.
+ mouseMoveIntervalID = setInterval(() => {
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target: div,
+ offsetX: (divRect.width / 4) * 3,
+ offsetY: (divRect.height / 4) * 3,
+ });
+ }, 100);
+ } else {
+ info("Got pointerlockchange for exiting");
+ }
+ });
+
+ function start() {
+ div = document.getElementById("div");
+ info("Requesting fullscreen on parent");
+ addFullscreenChangeContinuation("enter", async () => {
+ info("Got fullscreenchange for entering");
+ await promiseNativeMouseEvent({
+ type: "mousemove",
+ target: div,
+ offsetX: 0,
+ offsetY: 0,
+ });
+ div.addEventListener("mousemove", moveUnlocked);
+ // Bug 1295815
+ // Retrigger synthesizeNativeMouseEvent until it actually happens.
+ mouseMoveIntervalID = setInterval(() => {
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target: div,
+ atCenter: true,
+ });
+ }, 100);
+ });
+ div.requestFullscreen();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_suppressSomeMouseEvents.html b/dom/tests/mochitest/pointerlock/file_suppressSomeMouseEvents.html
new file mode 100644
index 0000000000..bc1e1e64a5
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_suppressSomeMouseEvents.html
@@ -0,0 +1,158 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+<head>
+ <title>Bug 633602 - file_cursorPosEvents.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #child {
+ width: 100px;
+ height: 100px;
+ background-color:Green;
+ }
+ </style>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602</a>
+
+ <div id="parent">
+ <div id="child"></div>
+ </div>
+
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Test will check to make sure that the following mouse events are no
+ * longer executed in pointer lock.
+ * - mouseover, mouseout, mouseenter, mouseleave
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ function PointerEventStats() {
+ this.mouseEnter = false;
+ this.mouseLeave = false;
+ this.mouseOver = false;
+ this.mouseOut = false;
+ }
+
+ var parent
+ , child
+ , parentStats = new PointerEventStats()
+ , childStats = new PointerEventStats()
+ , isPointerLocked = false;
+
+ function runTests () {
+ ok(isPointerLocked, "expected mouse to be locked, but wasn't.");
+
+ is(childStats.mouseEnter, false,
+ "child's mouseenter should not be firing in Full Screen and Pointer Lock.");
+ is(childStats.mouseOver, false,
+ "child's mouseover should not be firing in Full Screen and Pointer Lock.");
+ is(childStats.mouseLeave, false,
+ "child's mouseleave should not be firing in Full Screen and Pointer Lock.");
+ is(childStats.mouseOut, false,
+ "child's mouseout should not be firing in Full Screen and Pointer Lock.");
+
+ is(parentStats.mouseEnter, false,
+ "parent's mouseenter should not be firing in Full Screen and Pointer Lock.");
+ is(parentStats.mouseOver, false,
+ "parent's mouseover should not be firing in Full Screen and Pointer Lock.");
+ is(parentStats.mouseLeave, false,
+ "parent's mouseleave should not be firing in Full Screen and Pointer Lock.");
+ is(parentStats.mouseOut, false,
+ "parent's mouseout should not be firing in Full Screen and Pointer Lock.");
+ }
+
+ var parentMoveListener = function () {
+ isPointerLocked = !!document.pointerLockElement;
+ removeEventListeners();
+ document.exitPointerLock();
+ };
+
+ var parentOutListener = function (e) {
+ parentStats.mouseOut = true;
+ };
+ var parentLeaveListener = function (e) {
+ parentStats.mouseLeave = true;
+ };
+ var parentOverListener = function (e) {
+ parentStats.mouseOver = true;
+ };
+ var parentEnterListener = function (e) {
+ parentStats.mouseEnter = true;
+ };
+
+ var childOutListener = function (e) {
+ childStats.mouseOut = true;
+ };
+ var childLeaveListener = function (e) {
+ childStats.mouseLeave = true;
+ };
+ var childOverListener = function (e) {
+ childStats.mouseOver = true;
+ };
+ var childEnterListener = function (e) {
+ childStats.mouseEnter = true;
+ };
+
+ function addEventListeners() {
+ parent.addEventListener("mousemove", parentMoveListener);
+
+ parent.addEventListener("mouseout", parentOutListener);
+ parent.addEventListener("mouseleave", parentLeaveListener);
+ parent.addEventListener("mouseover", parentOverListener);
+ parent.addEventListener("mouseenter", parentEnterListener);
+
+ child.addEventListener("mouseout", childOutListener);
+ child.addEventListener("mouseleave", childLeaveListener);
+ child.addEventListener("mouseover", childOverListener);
+ child.addEventListener("mouseenter", childEnterListener);
+ }
+
+ function removeEventListeners() {
+ parent.removeEventListener("mousemove", parentMoveListener);
+
+ parent.removeEventListener("mouseout", parentOutListener);
+ parent.removeEventListener("mouseleave", parentLeaveListener);
+ parent.removeEventListener("mouseover", parentOverListener);
+ parent.removeEventListener("mouseenter", parentEnterListener);
+
+ child.removeEventListener("mouseout", childOutListener);
+ child.removeEventListener("mouseleave", childLeaveListener);
+ child.removeEventListener("mouseover" , childOverListener);
+ child.removeEventListener("mouseenter", childEnterListener);
+ }
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === parent) {
+ addEventListeners();
+ synthesizeMouseAtCenter(child, { type: "mousemove" }, window);
+ }
+ else {
+ addFullscreenChangeContinuation("exit", function() {
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ });
+
+ function start() {
+ parent = document.getElementById("parent");
+ child = document.getElementById("child");
+ addFullscreenChangeContinuation("enter", function() {
+ parent.requestPointerLock();
+ });
+ parent.requestFullscreen();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html b/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html
new file mode 100644
index 0000000000..4a6b9084bb
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+ -->
+ <head>
+ <title>Bug 633602 - file_targetOutOfFocus.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <p id="display"></p>
+ <div id="content">
+ </div>
+ <div id="div">
+ <input id="input" type="text" />
+ </div>
+ <pre id="test">
+ <script type="application/javascript">
+ /*
+ * Test for Bug 633602
+ * Element doesn't need to have focus to request
+ * pointer lock
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.getElementById("div")
+ , input = document.getElementById("input")
+ , divPointerLock = false;
+
+ function runTests () {
+ ok(divPointerLock, "Pointer should be locked even if " +
+ "the element being locked is not focused");
+ }
+
+ input.addEventListener("focus", function() {
+ div.requestPointerLock();
+ }, { once: true });
+
+ document.addEventListener("pointerlockchange", function (e) {
+ if (document.pointerLockElement === div) {
+ divPointerLock = true;
+ addFullscreenChangeContinuation("exit", function() {
+ runTests();
+ SimpleTest.finish();
+ });
+ document.exitFullscreen();
+ }
+ });
+
+ function start() {
+ addFullscreenChangeContinuation("enter", function() {
+ input.focus();
+ });
+ div.requestFullscreen();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/file_withoutDOM.html b/dom/tests/mochitest/pointerlock/file_withoutDOM.html
new file mode 100644
index 0000000000..c63a72cf9a
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/file_withoutDOM.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+
+ Test DOM tree in full screen
+ -->
+ <head>
+ <title>Bug 633602 - file_DOMtree.html</title>
+ <script src="/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript" src="pointerlock_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ </style>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <pre id="test">
+ <script type="text/javascript">
+ /*
+ * Test for Bug 633602
+ * Checks if element is attached to the DOM Tree before locking
+ * the pointer
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var div = document.createElement("div")
+ , withouthDOM = false;
+
+ function runTests () {
+ ok(withouthDOM, "If an element is NOT in the " +
+ "DOM Tree pointer should NOT be locked");
+ }
+
+ document.addEventListener("pointerlockerror", function (e) {
+ withouthDOM = true;
+ runTests();
+ SimpleTest.finish();
+ });
+
+ function start() {
+ div.requestPointerLock();
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/iframe_differentDOM.html b/dom/tests/mochitest/pointerlock/iframe_differentDOM.html
new file mode 100644
index 0000000000..0e3eac19b9
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/iframe_differentDOM.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <div id="div"></div><input>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/mochitest.ini b/dom/tests/mochitest/pointerlock/mochitest.ini
new file mode 100644
index 0000000000..54106104b2
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/mochitest.ini
@@ -0,0 +1,37 @@
+[DEFAULT]
+support-files =
+ pointerlock_utils.js
+ iframe_differentDOM.html
+ !/gfx/layers/apz/test/mochitest/apz_test_utils.js
+
+[test_closewindow-with-pointerlock.html]
+[test_pointerlock_target_not_in_active_document.html]
+[test_pointerlock-api.html]
+tags = fullscreen
+skip-if = (os == 'android') # Bug 1612553
+support-files =
+ file_pointerlock-api.html
+ file_pointerlock-api-with-shadow.html
+ file_pointerlockerror.html
+ file_escapeKey.html
+ file_withoutDOM.html
+ file_removedFromDOM.html
+ file_pointerLockPref.html
+ file_nestedFullScreen.html
+ file_doubleLock.html
+ file_childIframe.html
+ file_movementXY.html
+ file_infiniteMovement.html
+ file_retargetMouseEvents.html
+ file_targetOutOfFocus.html
+ file_screenClientXYConst.html
+ file_suppressSomeMouseEvents.html
+ file_locksvgelement.html
+ file_allowPointerLockSandboxFlag.html
+ file_changeLockElement.html
+[test_pointerlock_focus.html]
+[test_pointerlock_xorigin_iframe.html]
+support-files =
+ file_pointerlock_xorigin_iframe.html
+ file_pointerlock_xorigin_iframe_no_user_gesture.html
+ file_pointerlock_xorigin_iframe_not_focused.html
diff --git a/dom/tests/mochitest/pointerlock/pointerlock_utils.js b/dom/tests/mochitest/pointerlock/pointerlock_utils.js
new file mode 100644
index 0000000000..480f8b3c7d
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/pointerlock_utils.js
@@ -0,0 +1,99 @@
+// Get test filename for page being run in popup so errors are more useful
+var testName = location.pathname.split("/").pop();
+
+// Wrap test functions and pass to parent window
+window.ok = function (a, msg) {
+ opener.ok(a, testName + ": " + msg);
+};
+
+window.is = function (a, b, msg) {
+ opener.is(a, b, testName + ": " + msg);
+};
+
+window.isnot = function (a, b, msg) {
+ opener.isnot(a, b, testName + ": " + msg);
+};
+
+window.todo = function (a, msg) {
+ opener.todo(a, testName + ": " + msg);
+};
+
+window.todo_is = function (a, b, msg) {
+ opener.todo_is(a, b, testName + ": " + msg);
+};
+
+window.todo_isnot = function (a, b, msg) {
+ opener.todo_isnot(a, b, testName + ": " + msg);
+};
+
+window.info = function (msg) {
+ opener.info(testName + ": " + msg);
+};
+
+// Override bits of SimpleTest so test files work stand-alone
+var SimpleTest = SimpleTest || {};
+
+SimpleTest.waitForExplicitFinish = function () {
+ dump("[POINTERLOCK] Starting " + testName + "\n");
+};
+
+SimpleTest.finish = function () {
+ dump("[POINTERLOCK] Finishing " + testName + "\n");
+ opener.nextTest();
+};
+
+// Keep track of how many fullscreenChange enters we've received, so that
+// we can balance them with the number of exits we receive. We reset this
+// to 0 when we load a test.
+var fullscreenChangeEnters = 0;
+
+addLoadEvent(function () {
+ info(`Resetting fullscreen enter count.`);
+ fullscreenChangeEnters = 0;
+ if (typeof start !== "undefined") {
+ // Delay one event loop to stabilize the initial state of the page.
+ SimpleTest.executeSoon(start);
+ }
+});
+
+// Returns true if the window believes it is in fullscreen. This may be true even
+// before an asynchronous fullscreen transition is complete.
+function inFullscreenMode(win) {
+ return win.document.fullscreenElement;
+}
+
+// Adds a listener that will be called once a fullscreen transition
+// is complete. When type==='enter', callback is called when we've
+// received a fullscreenchange event, and the fullscreen transition is
+// complete. When type==='exit', callback is called when we've
+// received a fullscreenchange event and the window is out of
+// fullscreen. inDoc is the document which the listeners are added on,
+// if absent, the listeners are added to the current document.
+function addFullscreenChangeContinuation(type, callback, inDoc) {
+ var doc = inDoc || document;
+ var topWin = doc.defaultView.top;
+ function checkCondition() {
+ if (type == "enter") {
+ fullscreenChangeEnters++;
+ return inFullscreenMode(topWin);
+ } else if (type == "exit") {
+ fullscreenChangeEnters--;
+ return fullscreenChangeEnters
+ ? inFullscreenMode(topWin)
+ : !inFullscreenMode(topWin);
+ } else {
+ throw "'type' must be either 'enter', or 'exit'.";
+ }
+ }
+ function invokeCallback(event) {
+ // Use async call after a paint to workaround unfinished fullscreen
+ // change even when the window size has changed on Linux.
+ requestAnimationFrame(() => setTimeout(() => callback(event), 0), 0);
+ }
+ function onFullscreenChange(event) {
+ doc.removeEventListener("fullscreenchange", onFullscreenChange);
+ ok(checkCondition(), `Should ${type} fullscreen.`);
+ invokeCallback(event);
+ }
+ doc.addEventListener("fullscreenchange", onFullscreenChange);
+}
diff --git a/dom/tests/mochitest/pointerlock/test_closewindow-with-pointerlock.html b/dom/tests/mochitest/pointerlock/test_closewindow-with-pointerlock.html
new file mode 100644
index 0000000000..797228c3e0
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/test_closewindow-with-pointerlock.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1323983 - Auto-close window after holding pointerlock</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body style="width: 100vw; height: 100vh; margin: 0;">
+ <script>
+ if (!opener) {
+ SimpleTest.waitForExplicitFinish();
+ }
+
+ var newwin = null;
+ function finish() {
+ newwin.close()
+ setTimeout(function() {
+ SimpleTest.finish();
+ }, 0);
+ }
+
+ addLoadEvent(function() {
+ SimpleTest.waitForFocus(function() {
+ if (!opener) {
+ newwin = window.open(location);
+ } else {
+ document.addEventListener("pointerlockchange", function() {
+ opener.is(document.pointerLockElement, document.body,
+ "Check we have locked the pointer");
+ opener.finish();
+ }, {once: true});
+ document.addEventListener("pointerlockerror", function() {
+ opener.info("Fail to lock pointer");
+ opener.finish();
+ });
+ document.addEventListener("click", function() {
+ opener.info("Clicked");
+ document.body.requestPointerLock();
+ }, {once: true});
+ setTimeout(function() {
+ opener.info("Clicking");
+ synthesizeMouseAtCenter(document.body, {});
+ }, 0);
+ }
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/test_pointerlock-api.html b/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
new file mode 100644
index 0000000000..201fdbcb9c
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633602
+-->
+ <head>
+ <title>Test for Bug 633602</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
+ Mozilla Bug 633602
+ </a>
+ <div id="content">
+ </div>
+ <pre id="test">
+ <script type="application/javascript">
+ const { AppConstants } = SpecialPowers.ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+ );
+
+ /**
+ * Pointer Lock tests for bug 633602. These depend on the fullscreen api
+ * which doesn't work when run in the mochitests' iframe, since the
+ * mochitests' iframe doesn't have an allowfullscreen attribute. To get
+ * around this, all tests are run in a child window, which can go fullscreen.
+ * This method is borrowed from dom/html/test/test_fullscreen-api.html.
+ **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"]
+ ]}, nextTest);
+
+ // Run the tests which go full-screen in new window, as Mochitests
+ // normally run in an iframe, which by default will not have the
+ // allowfullscreen attribute set, so full-screen won't work.
+ var gTestFiles = [
+ "file_screenClientXYConst.html",
+ "file_childIframe.html",
+ "file_doubleLock.html",
+ "file_escapeKey.html",
+ "file_infiniteMovement.html",
+ "file_locksvgelement.html",
+ "file_movementXY.html",
+ "file_nestedFullScreen.html",
+ "file_pointerlock-api.html",
+ "file_pointerlock-api-with-shadow.html",
+ "file_pointerlockerror.html",
+ "file_pointerLockPref.html",
+ "file_removedFromDOM.html",
+ "file_retargetMouseEvents.html",
+ "file_suppressSomeMouseEvents.html",
+ "file_targetOutOfFocus.html",
+ "file_withoutDOM.html",
+ "file_allowPointerLockSandboxFlag.html",
+ "file_changeLockElement.html",
+ ];
+
+ var gDisableList = [
+ // Bug 1615802
+ { file: "file_screenClientXYConst.html", platform: "macosx" },
+ // Bug 1357082
+ { file: "file_retargetMouseEvents.html", platform: "all" },
+ ];
+
+ var gTestWindow = null;
+ var gTestIndex = 0;
+
+ function nextTest() {
+ if (gTestWindow) {
+ gTestWindow.close();
+ }
+
+ // Try to stabilize the state before running the next test.
+ SimpleTest.waitForFocus(
+ () => requestAnimationFrame(() => setTimeout(runNextTest)));
+ }
+
+ function runNextTest() {
+ if (gTestIndex < gTestFiles.length) {
+ var file = gTestFiles[gTestIndex];
+ gTestIndex++;
+
+ var skipTest = false;
+ for (var item of gDisableList) {
+ if (item.file == file &&
+ ("all" == item.platform || AppConstants.platform == item.platform)) {
+ skipTest = true;
+ break;
+ }
+ }
+ if (!skipTest) {
+ info(`Testing ${file}`);
+ gTestWindow = window.open(file, "", "width=500,height=500");
+ } else {
+ info(`Skip ${file}`);
+ nextTest();
+ }
+ } else {
+ SimpleTest.finish();
+ }
+ }
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/test_pointerlock_focus.html b/dom/tests/mochitest/pointerlock/test_pointerlock_focus.html
new file mode 100644
index 0000000000..4b414d3ba6
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock_focus.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1646493 - test_pointerlock_focus.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <style>
+ #target {
+ width: 50px;
+ height: 50px;
+ background-color: green;
+ }
+
+ iframe {
+ width: 250px;
+ height: 50px;
+ }
+ </style>
+</head>
+<body style="width: 100vw; height: 100vh; margin: 0;">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1646493">Mozilla Bug 1646493</a><br>
+<div id="target"></div>
+<input id="input"><br>
+<iframe id="iframe" src="https://example.com/tests/dom/tests/mochitest/pointerlock/iframe_differentDOM.html"></iframe>
+<script>
+
+function waitForEventOnce(aTarget, aEvent) {
+ return new Promise(aResolve => {
+ aTarget.addEventListener(aEvent, aResolve, { once: true });
+ });
+}
+
+function unexpectedEvent(aEvent) {
+ ok(false, `Unexpected ${aEvent.type} event`);
+}
+
+async function requestPointerLock(aElement) {
+ let doc = aElement.ownerDocument;
+ doc.addEventListener("pointerlockerror", unexpectedEvent);
+
+ SpecialPowers.wrap(doc).notifyUserGestureActivation();
+ aElement.requestPointerLock();
+ await waitForEventOnce(doc, "pointerlockchange");
+
+ is(doc.pointerLockElement, aElement, "target pointer locked");
+ doc.removeEventListener("pointerlockerror", unexpectedEvent);
+}
+
+async function exitPointerLock(aDocument) {
+ if (aDocument.pointerLockElement) {
+ aDocument.exitPointerLock();
+ await waitForEventOnce(aDocument, "pointerlockchange");
+ }
+ is(aDocument.pointerLockElement, null, "pointer unlocked");
+}
+
+let target = document.getElementById("target");
+let input = document.getElementById("input");
+
+add_task(async function init() {
+ await SimpleTest.promiseFocus();
+});
+
+add_task(async function focusMovesIntoXoriginIframe() {
+ let iframe = document.getElementById("iframe");
+
+ input.focus();
+ is(document.activeElement, input, "focus input");
+
+ // Request pointer lock on parent window
+ await requestPointerLock(target);
+
+ // Move focus into child window with different origin
+ let win = iframe.contentWindow;
+ document.addEventListener("pointerlockchange", unexpectedEvent);
+ synthesizeKey("KEY_Tab");
+ await SpecialPowers.spawn(win, [], async () => {
+ if (!content.document.hasFocus()) {
+ await new Promise((resolve) => {
+ content.document.addEventListener('focus', resolve, { once: true });
+ });
+ }
+ });
+ is(document.pointerLockElement, target, "target pointer locked");
+ document.removeEventListener("pointerlockchange", unexpectedEvent);
+
+ // Exit pointer lock
+ await exitPointerLock(document);
+});
+
+add_task(async function focusMovesToAnotherTab() {
+ input.focus();
+ is(document.activeElement, input, "focus input");
+
+ // Request pointer lock on parent window
+ await requestPointerLock(target);
+
+ // Move focus to another tab
+ let promise = waitForEventOnce(document, "pointerlockchange");
+ let win = window.open('iframe_differentDOM.html');
+ await promise;
+ is(document.pointerLockElement, null, "pointer unlocked");
+
+ win.close();
+ await exitPointerLock(document);
+});
+</script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/test_pointerlock_target_not_in_active_document.html b/dom/tests/mochitest/pointerlock/test_pointerlock_target_not_in_active_document.html
new file mode 100644
index 0000000000..8629230b6b
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock_target_not_in_active_document.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1646493 - test_pointerlock_target_not_in_active_document.html</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body style="width: 100vw; height: 100vh; margin: 0;">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1646493">Mozilla Bug 1646493</a><br>
+<iframe></iframe>
+<script>
+if (!opener) {
+ SimpleTest.waitForExplicitFinish();
+}
+
+var newwin = null;
+function finish() {
+ newwin.close()
+ setTimeout(function() {
+ SimpleTest.finish();
+ }, 0);
+}
+
+function testUnfocusedDocument() {
+ opener.document.addEventListener("pointerlockchange", function() {
+ opener.ok(false, "Check we have locked the pointer");
+ opener.document.exitPointerLock();
+ opener.testInactiveTopLevelDocument();
+ }, {once: true});
+
+ opener.document.addEventListener("pointerlockerror", function() {
+ opener.ok(true, "Fail to lock pointer");
+ opener.testInactiveTopLevelDocument();
+ }, {once: true});
+
+ SpecialPowers.wrap(opener.document).notifyUserGestureActivation();
+ opener.document.body.requestPointerLock();
+}
+
+function testInactiveTopLevelDocument() {
+ let iframeWin = newwin.frames[0];
+ let iframeDoc = iframeWin.document;
+
+ iframeDoc.addEventListener("pointerlockchange", function() {
+ ok(false, "Check we have locked the pointer");
+ iframeDoc.exitPointerLock();
+ finish();
+ }, {once: true});
+
+ iframeDoc.addEventListener("pointerlockerror", function() {
+ ok(true, "Fail to lock pointer");
+ finish();
+ }, {once: true});
+
+ SpecialPowers.wrap(iframeDoc).notifyUserGestureActivation();
+
+ newwin.addEventListener("pagehide", function() {
+ iframeDoc.body.requestPointerLock();
+ }, {once: true});
+ newwin.location = "iframe_differentDOM.html";
+}
+
+addLoadEvent(async function() {
+ await SimpleTest.promiseFocus();
+
+ if (!opener) {
+ newwin = window.open(location);
+ return;
+ }
+
+ testUnfocusedDocument();
+});
+</script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html b/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html
new file mode 100644
index 0000000000..fb4f84470e
--- /dev/null
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1662587
+-->
+<head>
+<title>Test for Bug 1662587</title>
+<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1662587">Mozilla Bug 1662587</a>
+<div id="content"></div>
+<pre id="test">
+<script type="application/javascript">
+/**
+ * Pointer Lock tests for bug 1662587.
+ **/
+
+SimpleTest.waitForExplicitFinish();
+
+let gTestFiles = [
+ "file_pointerlock_xorigin_iframe.html",
+ "file_pointerlock_xorigin_iframe_no_user_gesture.html",
+ "file_pointerlock_xorigin_iframe_not_focused.html",
+];
+
+let gTestWindow = null;
+let gTestIndex = 0;
+
+SpecialPowers.pushPrefEnv({"set": [
+ // This will make dispatched event going through parent process.
+ ["test.events.async.enabled", true]
+]}, nextTest);
+
+function nextTest() {
+ if (gTestWindow) {
+ gTestWindow.close();
+ }
+ SimpleTest.waitForFocus(runNextTest);
+}
+
+function runNextTest() {
+ if (gTestIndex < gTestFiles.length) {
+ let file = gTestFiles[gTestIndex];
+ gTestIndex++;
+
+ info(`Testing ${file}`);
+ gTestWindow = window.open(file, "", "width=500,height=500");
+ info(`Test finish`);
+ } else {
+ SimpleTest.finish();
+ }
+}
+</script>
+</pre>
+</body>
+</html>