wpt / pointerevents / pointerevent_movementxy.html?mouse

Open test on wpt.live | wpt.fyi

Runs: Chrome 158.0.8110.0 (wpt@850b36a56, 2026-10-18) | Firefox 160.0a1 (wpt@b58866519, 2026-10-18) | Safari 254 preview (wpt@850b36a56, 2026-10-18) | Ladybird 1.0-1706f (wpt@850b36a56, 2026-10-18) | Servo Servo 0.7 (wpt@df642c414, 2026-10-17) | Blitz fb8d6c15e (wpt@cd1d8aa5b, 2026-10-17)

ChromeFirefoxSafariLadybirdServoBlitz
Total1/11/11/10/10/1NOT RUN
SubtestChromeFirefoxSafariLadybirdServoBlitz
mouse pointerevent attributesPASSPASSPASSFAILFAIL—

/pointerevents/pointerevent_movementxy.html

<!doctype html>
<html>
    <head>
        <title>Pointer Events properties tests</title>
        <meta name="timeout" content="long">
        <meta name="viewport" content="width=device-width">
        <meta name="variant" content="?mouse">
        <meta name="variant" content="?touch">
        <meta name="variant" content="?pen">
        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
        <script src="/resources/testharness.js"></script>
        <script src="/resources/testharnessreport.js"></script>
        <script src="/resources/testdriver.js"></script>
        <script src="/resources/testdriver-actions.js"></script>
        <script src="/resources/testdriver-vendor.js"></script>
        <!-- Additional helper script for common checks across event types -->
        <script type="text/javascript" src="pointerevent_support.js"></script>
        <style>
          #testContainer {
            touch-action: none;
            user-select: none;
            position: relative;
          }
          #box1 {
            top: 30px;
            left: 50px;
            background: black;
          }
          #box2 {
            top: 70px;
            left: 250px;
            background: red;
          }
          #innerFrame {
            top: 10px;
            left: 100px;
          }
          #square2 {
            visibility: block;
          }
        </style>
        <script>
            var inputSource = location.search.substring(1);
            var expectedPointerId = NaN;
            var lastScreenX = null;
            var lastScreenY = null;
            var lastEventDocument = null;
            var actionsPromise;

            function resetTestState() {
                lastScreenX = null;
                lastScreenY = null;
                lastEventDocument = null;
            }

            function loadFrame() {
                return new Promise(resolve => {
                    const frame = document.getElementById('innerFrame');
                    frame.onload = resolve;
                    frame.srcdoc = `<!doctype html>
                      <meta name='viewport' content='width=device-width'>`;
                });
            }

            var nonPointermoveEventList = [
                  "pointerover",
                  "pointerenter",
                  "pointerdown",
                  "pointerup",
                  "pointerout",
                  "pointerleave",
                  "gotpointercapture",
                  "lostpointercapture"];

            function injectInput(pointerType) {
                var pointerId = pointerType + "Pointer1";
                return new test_driver.Actions()
                                     .addPointer(pointerId, pointerType)
                                     .pointerMove(0, 0, {origin: box1})
                                     .pointerDown()
                                     // Moves in top frame (before iframe)
                                     .pointerMove(15, 20, {origin: box1})
                                     .addTick()
                                     .pointerMove(30, 30, {origin: box1})
                                     .addTick()
                                     // Move entering iframe
                                     .pointerMove(50, 40, {origin: box1})
                                     .addTick()
                                     // Moves inside iframe
                                     .pointerMove(80, 30, {origin: box1})
                                     .addTick()
                                     .pointerMove(110, 20, {origin: box1})
                                     .addTick()
                                     // Move exiting iframe
                                     .pointerMove(0, 0, {origin: box2})
                                     .addTick()
                                     // Moves in top frame (after iframe)
                                     .pointerMove(5, 5, {origin: box2})
                                     .addTick()
                                     .pointerMove(-5, -5, {origin: box2})
                                     .pointerUp()
                                     .send();
            }

            async function run() {
                var test_pointerEvent = setup_pointerevent_test(
                      "pointerevent attributes", [inputSource]);

                await loadFrame();

                [
                  document,
                  document.getElementById('innerFrame').contentDocument
                ].forEach(function(element) {
                  nonPointermoveEventList.forEach(function(eventName) {
                    on_event(element, eventName, function (event) {
                      if (lastScreenX && lastScreenY) {
                        test_pointerEvent.step(function() {
                          assert_equals(event.movementX, 0,
                                "movementX should be 0 for event other than pointermove.");
                          assert_equals(event.movementY, 0,
                                "movementY should be 0 for event other than pointermove.");
                        });
                      }
                    });
                  });

                  on_event(element, 'pointermove', function (event) {
                    test_pointerEvent.step(function() {
                      if (lastScreenX && lastScreenY &&
                            lastEventDocument === event.view.document) {
                        assert_equals(event.movementX, event.screenX - lastScreenX,
                              "movementX should be the delta between current event's and last event's screenX");
                        assert_equals(event.movementY, event.screenY - lastScreenY,
                              "movementY should be the delta between current event's and last event's screenY");
                      }
                    });
                    lastScreenX = event.screenX;
                    lastScreenY = event.screenY;
                    lastEventDocument = event.view.document;
                  });
                });
                on_event(document.querySelector('#box1'), 'pointerdown', event => {
                  event.target.releasePointerCapture(event.pointerId);
                  test_pointerEvent.step(function() {
                      assert_equals(event.pointerType, expectedPointerType,
                            "Use the instructed pointer type.");
                  });
                  lastScreenX = event.screenX;
                  lastScreenY = event.screenY;
                  lastEventDocument = event.view.document;
                });
                on_event(document.querySelector('#box2'), 'pointerup', event => {
                  // Make sure the test finishes after all the input actions.
                  actionsPromise.then( () => {
                    test_pointerEvent.done();
                  });
                });

                // Inject input
                actionsPromise = injectInput(inputSource);
            }

        </script>
    </head>
    <body onload="run()">
        <h1>Pointer Events movementX/Y attribute test</h1>
        <h2 id="pointerTypeDescription"></h2>
        <h4>
            Test Description: This test checks the movementX/Y properties of pointer events.
            <ol>
                 <li>Press down on the black square.</li>
                 <li>Move your pointer  slowly along a straight line to the red square.</li>
                 <li>Release the pointer when you are over the red square.</li>
            </ol>

            Test passes if the proper behavior of the events is observed.
        </h4>
        <div id="testContainer">
            <div id="box1" class="square"></div>
            <div id="box2" class="square"></div>
            <iframe id="innerFrame"></iframe>
        </div>
        <div class="spacer"></div>
    </body>
</html>