summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/uievents/mouse
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/uievents/mouse
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/uievents/mouse')
-rw-r--r--testing/web-platform/tests/uievents/mouse/attributes.html61
-rw-r--r--testing/web-platform/tests/uievents/mouse/cancel-mousedown-in-subframe.html64
-rw-r--r--testing/web-platform/tests/uievents/mouse/layout_change_should_fire_mouseover.html97
-rw-r--r--testing/web-platform/tests/uievents/mouse/mouse_buttons_back_forward.html56
-rw-r--r--testing/web-platform/tests/uievents/mouse/mouseenter-mouseleave-on-drag.html187
-rw-r--r--testing/web-platform/tests/uievents/mouse/mouseevent_move_button.html102
-rw-r--r--testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html93
-rw-r--r--testing/web-platform/tests/uievents/mouse/resources/mouse-event-reporter-subframe.html37
-rw-r--r--testing/web-platform/tests/uievents/mouse/resources/utils.js27
9 files changed, 724 insertions, 0 deletions
diff --git a/testing/web-platform/tests/uievents/mouse/attributes.html b/testing/web-platform/tests/uievents/mouse/attributes.html
new file mode 100644
index 0000000000..bbc388445c
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/attributes.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<title>MouseEvent attributes</title>
+<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>
+<script src="resources/utils.js"></script>
+
+<span id="left">Left</span> <span id="right">Right</span>
+
+<script>
+ 'use strict';
+
+ const event_list = ["mouseover", "mouseenter", "mousemove",
+ "mousedown", "mouseup", "mouseout", "mouseleave"];
+
+ promise_test(async () => {
+ let left = document.getElementById("left");
+ let right = document.getElementById("right");
+
+ let event_promises = [];
+ event_list.forEach(ename => event_promises.push(getEvent(ename, right)));
+
+ let actions = new test_driver.Actions().addPointer("TestPointer", "mouse")
+ .pointerMove(0, 0, {origin: left})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: right})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: left})
+ .pointerDown()
+ .pointerUp()
+ await actions.send();
+
+ for (let i = 0; i < event_promises.length; i++) {
+ let e = await event_promises[i];
+
+ assert_equals(e.constructor, window.MouseEvent,
+ e.type + " should use a MouseEvent constructor");
+ assert_true(e instanceof MouseEvent,
+ e.type + " should be a MouseEvent");
+
+ assert_true(e.isTrusted,
+ e.type + ".isTrusted attribute");
+
+ assert_equals(e.composed,
+ e.type != 'mouseenter' && e.type != 'mouseleave',
+ e.type + ".composed attribute");
+
+ assert_equals(e.bubbles,
+ e.type != 'mouseenter' && e.type != 'mouseleave',
+ e.type + ".bubbles attribute");
+
+ assert_equals(e.cancelable,
+ e.type != 'mouseenter' && e.type != 'mouseleave',
+ e.type + ".cancelable attribute");
+ }
+ }, "MouseEvent attributes");
+</script>
diff --git a/testing/web-platform/tests/uievents/mouse/cancel-mousedown-in-subframe.html b/testing/web-platform/tests/uievents/mouse/cancel-mousedown-in-subframe.html
new file mode 100644
index 0000000000..0735037735
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/cancel-mousedown-in-subframe.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="resources/utils.js"></script>
+<body>
+ <div>
+ Dragging the mouse from child frame to parent frame causes both <code>mousedown</code> and
+ <code>mouseup</code> events being dispatched to child frame, regardless of whether the
+ <code>mousedown</code> event is canceled or not.
+ </div>
+ <iframe id="child_frame" width="300px" height="40px"
+ src="resources/mouse-event-reporter-subframe.html">
+ </iframe>
+</body>
+<script>
+ "use strict"
+ let topframe_loaded = getEvent("load", window);
+ let subframe_loaded = getMessageData("load", frames[0]);
+
+ let top_frame_mousedown;
+ let top_frame_mouseup;
+
+ promise_setup(async () => {
+ await topframe_loaded;
+ await subframe_loaded;
+
+ window.addEventListener("mousedown", e => top_frame_mousedown = true);
+ window.addEventListener("mouseup", e => top_frame_mouseup = true);
+ });
+
+ [false, true].forEach(cancel_mousedown => {
+ let mousedown_msg = cancel_mousedown ? "canceled" : "not-canceled";
+
+ promise_test(async () => {
+ top_frame_mousedown = false;
+ top_frame_mouseup = false;
+
+ sendMessage(frames[0], "cancel-mousedown", cancel_mousedown);
+
+ const mousedown_promise = getMessageData("mousedown", frames[0]);
+ const mouseup_promise = getMessageData("mouseup", frames[0]);
+
+ const child_frame = document.getElementById("child_frame");
+ const actions_promise = new test_driver.Actions()
+ .pointerMove(5, 5, {origin: child_frame})
+ .pointerDown()
+ .pointerMove(5, 5, {origin: document.body})
+ .pointerUp()
+ .send();
+
+ await actions_promise;
+
+ let mousedown_message = await mousedown_promise;
+ let mouseup_message = await mouseup_promise;
+
+ assert_equals(mousedown_message.param, mousedown_msg, "Child frame canceled mousedown?");
+ assert_false(top_frame_mousedown, "Top frame received mousedown?");
+ assert_false(top_frame_mouseup, "Top frame received mouseup?");
+ }, "Child frame receives mousedown/mouseup when mousedown is " + mousedown_msg);
+ });
+</script>
diff --git a/testing/web-platform/tests/uievents/mouse/layout_change_should_fire_mouseover.html b/testing/web-platform/tests/uievents/mouse/layout_change_should_fire_mouseover.html
new file mode 100644
index 0000000000..49257ae60d
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/layout_change_should_fire_mouseover.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Mouseover/enter is sent on layout change</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <style>
+ #spacer {
+ height: 100px;
+ width: 100px;
+ }
+ #red {
+ background-color: rgb(255, 0, 0);
+ position: absolute;
+ z-index: 0;
+ left: 0px;
+ top: 0px;
+ height: 100px;
+ width: 100px;
+ }
+ #blue {
+ background-color: rgb(0, 0, 255);
+ position: absolute;
+ z-index: 1;
+ left: 0px;
+ top: 0px;
+ height: 100px;
+ width: 100px;
+ }
+ #blue:hover {
+ background-color: rgb(255, 255, 0);
+ }
+ </style>
+ </head>
+ <body onload="run();">
+ <div id="spacer"></div>
+ <div id="red"></div>
+ <h4>Test Description: Tests that the mouseover event is fired and the element has a hover effect when the element underneath the mouse cursor is changed.
+ <ol>
+ <li>Put your mouse over the red rectangle</li>
+ <li>Click the primary mouse button</li>
+ </ol>
+ </h4>
+ <script type="text/javascript">
+ var testMouseOver = async_test('Tests that the mouseover event is fired and the element has a hover effect when the element underneath the mouse cursor is changed.');
+ var actions_promise;
+
+ var eventList = [];
+ function addBlue() {
+ document.body.innerHTML += '<div id="blue"></div>';
+ var blue = document.getElementById("blue");
+ var events = ['mouseover', 'mousemove', 'mouseout', 'mouseenter', 'mouseleave'];
+ events.forEach(function (event) {
+ blue.addEventListener(event, checkHoverEffect);
+ });
+ testMouseOver.step_timeout(function () {
+ checkEventSequence();
+ }, 2500);
+ }
+
+ function checkEventSequence() {
+ var result = eventList.join();
+ assert_equals(result, 'mouseover,mouseenter');
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ testMouseOver.done();
+ });
+ }
+
+ function run() {
+ document.addEventListener('click', addBlue);
+ }
+
+ function checkHoverEffect(event) {
+ eventList.push(event.type);
+ testMouseOver.step(function () {
+ assert_equals(event.target.id, "blue");
+ assert_equals(getComputedStyle(event.target).backgroundColor, "rgb(255, 255, 0)");
+ if (event.type == "mouseenter") {
+ checkEventSequence();
+ }
+ });
+ }
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: red})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/uievents/mouse/mouse_buttons_back_forward.html b/testing/web-platform/tests/uievents/mouse/mouse_buttons_back_forward.html
new file mode 100644
index 0000000000..2323bc1026
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/mouse_buttons_back_forward.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Mouse Button Back/Forward</title>
+ <link rel="author" title="Google" href="http://www.google.com/" />
+ <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>
+ <script>
+ var testMouseUp = async_test('Tests that the mouseup is preventable.');
+ var received_back = false;
+ var received_forward = false;
+ const backButton = 3;
+ const forwardButton = 4;
+ var actions_promise;
+ window.addEventListener('mouseup', function(e) {
+ if (e.button == backButton) {
+ received_back = true;
+ e.preventDefault();
+ } else if (e.button == forwardButton) {
+ received_forward = true;
+ e.preventDefault();
+ }
+ if (received_back && received_forward) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ testMouseUp.done();
+ });
+ }
+ });
+
+ function inject_input() {
+ // First click on back button and then forward button.
+ var actions = new test_driver.Actions();
+ actions_promise = actions.pointerMove(0, 0, {origin: target})
+ .pointerDown({button: actions.ButtonType.BACK})
+ .pointerUp({button: actions.ButtonType.BACK})
+ .pointerDown({button: actions.ButtonType.FORWARD})
+ .pointerUp({button: actions.ButtonType.FORWARD})
+ .send();
+ }
+ </script>
+
+ </head>
+ <body id="target" onload="inject_input()">
+ <h4>Test Description: Tests that the mouseup event is prevented.
+ <ol>
+ <li>Click the back mouse button</li>
+ <li>Click the back mouse forward</li>
+ </ol>
+ </h4>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/uievents/mouse/mouseenter-mouseleave-on-drag.html b/testing/web-platform/tests/uievents/mouse/mouseenter-mouseleave-on-drag.html
new file mode 100644
index 0000000000..c36a1501c0
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/mouseenter-mouseleave-on-drag.html
@@ -0,0 +1,187 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for redundant mouseenter or mouseleave events</title>
+ <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>
+</head>
+<style>
+#outer {
+ background: grey;
+ position: absolute;
+ left: 100px;
+ top: 100px;
+ width: 100px;
+ height: 100px;
+}
+#inner {
+ background: red;
+ position: absolute;
+ left: 30px;
+ top: 30px;
+ width: 40px;
+ height: 40px;
+}
+</style>
+
+<body>
+ <!-- Verifies that dragging mouse in/out of an element doesn't fire redundant
+ mouseenter or mouseleave events (crbug.com/356090 & crbug.com/470258) -->
+ <div id="outer">
+ <div id="inner"></div>
+ </div>
+</body>
+<script>
+let eventLog = [];
+let nextUncheckedEventIndex = 0;
+
+// Ensure match to the next sequence of events in the event log.
+function assert_next_events(target, expectedEventNames, message) {
+ for (let i = 0; i < expectedEventNames.length; i++) {
+ assert_true(nextUncheckedEventIndex < eventLog.length,
+ `${message}: empty event queue`);
+ const observed = eventLog[nextUncheckedEventIndex++];
+ const expected = `${expectedEventNames[i]}@${target.id}`;
+ assert_equals(observed, expected,`${message}: Event mismatch`);
+ }
+}
+
+// After validating the expected events, all entries in the event map
+// must be false or we have recorded an unexpected event.
+function assert_empty_event_queue(message) {
+ const uncheckedEvents = eventLog.length - nextUncheckedEventIndex;
+ assert_equals(uncheckedEvents, 0,
+ `${message}: Unexpected events ` +
+ `${eventLog.slice(-uncheckedEvents).join(", ")}`);
+}
+
+function addEventListeners(test) {
+ const eventTypes = [
+ 'mousedown',
+ 'mouseenter',
+ 'mouseleave',
+ 'mousemove',
+ 'mouseout',
+ 'mouseover',
+ 'mouseup'
+ ];
+ ['inner', 'outer'].forEach(id => {
+ const element = document.getElementById(id);
+ eventTypes.forEach(eventType => {
+ const listener = (e) => {
+ if (e.eventPhase == Event.AT_TARGET) {
+ eventLog.push(`${eventType}@${id}`);
+ }
+ };
+ element.addEventListener(eventType, listener);
+ test.add_cleanup(() => {
+ element.removeEventListener(eventType, listener);
+ });
+ })
+ });
+}
+
+// At the end of each action sequence we move the mouse over the root element.
+// Once this event is detected, all other upstream events must be logged and
+// we can proceed with the checks.
+async function mousemoveOverRootElement() {
+ return new Promise(resolve => {
+ const listener = (e) => {
+ if (e.eventPhase == Event.AT_TARGET) {
+ document.documentElement.removeEventListener('mousemove', listener);
+ resolve();
+ }
+ };
+ document.documentElement.addEventListener('mousemove', listener);
+ });
+}
+
+window.onload = async () => {
+ const outer = document.getElementById('outer');
+ const inner = document.getElementById('inner');
+ const leftOuter = 100;
+ const rightOuter = 200;
+ const leftInner = 130;
+ const rightInner = 170;
+ const centerY = 150;
+
+ promise_test(async t => {
+ addEventListeners(t);
+ const completionPromise = mousemoveOverRootElement();
+ const actions =new test_driver.Actions();
+ actions.pointerMove(leftOuter + 10, centerY)
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerMove(rightOuter - 10, centerY)
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .pointerMove(0, 0)
+ .send();
+ await actions;
+ await completionPromise;
+
+ assert_next_events(outer, ['mouseover', 'mouseenter', 'mousemove'],
+ 'Move over outer element');
+ assert_next_events(outer, ['mousedown', 'mousemove', 'mouseup'],
+ 'Drag across outer element');
+ assert_next_events(outer, ['mouseout', 'mouseleave'],
+ 'Move to origin');
+ assert_empty_event_queue('Drag across outer element');
+ }, 'Test dragging across inner div');
+
+ promise_test(async t => {
+ addEventListeners(t);
+ const completionPromise = mousemoveOverRootElement();
+ const actions =new test_driver.Actions();
+ actions.pointerMove(leftOuter + 10, centerY)
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerMove(leftInner + 10, centerY)
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .pointerMove(0, 0)
+ .send();
+ await actions;
+ await completionPromise;
+
+ assert_next_events(outer, ['mouseover', 'mouseenter', 'mousemove'],
+ 'Move over outer element');
+ assert_next_events(outer, ['mousedown', 'mouseout'],
+ 'Initiate drag');
+ assert_next_events(inner,
+ ['mouseover', 'mouseenter', 'mousemove', 'mouseup'],
+ 'Drag into inner element');
+ assert_next_events(inner, ['mouseout', 'mouseleave'],
+ 'Move to origin');
+ assert_next_events(outer, [ 'mouseleave'],
+ 'Move to origin');
+ assert_empty_event_queue('Drag into inner element');
+ }, 'Test dragging into inner div');
+
+ promise_test(async t => {
+ addEventListeners(t);
+ const completionPromise = mousemoveOverRootElement();
+ const actions =new test_driver.Actions();
+ actions.pointerMove(leftInner + 10, centerY)
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerMove(rightInner + 10, centerY)
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .pointerMove(0, 0)
+ .send();
+ await actions;
+ await completionPromise;
+
+ assert_next_events(inner, ['mouseover'], 'Move over inner element');
+ assert_next_events(outer, ['mouseenter'], 'Enter outer');
+ assert_next_events(inner, ['mouseenter', 'mousemove'],
+ 'Move across inner element');
+ assert_next_events(inner, ['mousedown', 'mouseout', 'mouseleave'],
+ 'Drag out of inner');
+ assert_next_events(outer, ['mouseover', 'mousemove', 'mouseup'],
+ 'Drag into outer');
+ assert_next_events(outer, ['mouseout', 'mouseleave'],
+ 'Move to origin');
+ assert_empty_event_queue('Drag into inner element');
+ }, 'Test dragging out of inner div');
+};
+</script>
+</html>
diff --git a/testing/web-platform/tests/uievents/mouse/mouseevent_move_button.html b/testing/web-platform/tests/uievents/mouse/mouseevent_move_button.html
new file mode 100644
index 0000000000..edde11d022
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/mouseevent_move_button.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Mouse Events with button depressed</title>
+ <meta name="timeout" content="long">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <style>
+ div.box {
+ border: 2px solid lightgray;
+ margin: 25px;
+ padding: 25px;
+ float: left;
+ }
+ #lightyellow {
+ background-color: lightyellow;
+ }
+ #lightblue {
+ background-color: lightblue;
+ }
+ #lightgreen {
+ background-color: lightgreen;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Mouse Events</h2>
+ <h4>Test Description: This test checks if mouse events set button property correctly
+ <ol>
+ <li>Put your mouse over the green rectangle</li>
+ <li>Press a non-primary button and hold it</li>
+ <li>Drag mouse to blue rectangle</li>
+ <li>Release mouse button</li>
+ </ol>
+ </h4>
+ <div class="box" id="lightyellow">
+ <div class="box" id="lightgreen"></div>
+ <div class="box" id="lightblue"></div>
+ </div>
+ <script>
+ var test = async_test("mouse events fired without button state");
+ var button = -1;
+ var actions_promise;
+
+ function run() {
+ var lightgreen = document.getElementById("lightgreen");
+ var lightyellow = document.getElementById("lightyellow");
+ var lightblue = document.getElementById("lightblue");
+
+ on_event(lightgreen, "contextmenu", function (event) {
+ event.preventDefault();
+ });
+
+ on_event(lightgreen, "mousedown", function (event) {
+ test.step(function() {assert_equals(button, -1, "There must only be one mouse down event.");});
+ test.step(function() {assert_not_equals(event.button, 0, "Must not be primary button.");});
+ button = event.button;
+ });
+ on_event(lightyellow, "click", function (event) {
+ test.step(function() {assert_equals(event.button, button, "Button must be the same as mousedown.");});
+ });
+ on_event(lightyellow, "mousemove", function (event) {
+ if (button != -1) {
+ test.step(function() {assert_equals(event.button, 0, "Button must be un-initialized for mousemove.");});
+ }
+ });
+ on_event(lightgreen, "mouseleave", function (event) {
+ if (button != -1) {
+ test.step(function() {assert_equals(event.button, 0, "Button must be un-initialized for mouseleave.");});
+ }
+ });
+ on_event(lightblue, "mouseenter", function (event) {
+ if (button != -1) {
+ test.step(function() {assert_equals(event.button, 0, "Button must be un-initialized for mouseenter.");});
+ }
+ });
+ on_event(lightblue, "mouseup", function (event) {
+ if (button != -1) {
+ test.step(function() {assert_equals(event.button, button, "Button must be the same as mousedown.");});
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test.done();
+ });
+ }
+ });
+
+ // Inject mouse inputs.
+ var actions = new test_driver.Actions();
+ actions_promise = actions.pointerMove(0, 0, {origin: lightgreen})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerMove(0, 0, {origin: lightyellow})
+ .pointerMove(0, 0, {origin: lightblue})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .send();
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html b/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html
new file mode 100644
index 0000000000..376a110fa5
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>mousemove event: preventDefault()</title>
+<link rel="author" title="Mirko Brodesser" href="mailto:mbrodesser@mozilla.com">
+<link rel="help" href="https://github.com/w3c/uievents/issues/278">
+<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>
+<script src=resources/utils.js></script>
+
+<body>
+ <div id="a">div a</div>
+ <div id="b">div b</div>
+ <div id="c" draggable="true">div c</div>
+</body>
+
+<script>
+ 'use strict';
+
+ let event_log = [];
+
+ function logEvents(e) {
+ event_log.push(e.type);
+ }
+
+ // Deliberately avoiding mouseup here because the last selectionchange
+ // may be fired before or after the mouseup.
+ ["mousedown", "mousemove", "selectionchange", "dragstart"].forEach(ename => {
+ document.addEventListener(ename, logEvents);
+ });
+ document.addEventListener("mousemove", e => e.preventDefault());
+
+ promise_test(async () => {
+ event_log = [];
+
+ const a = document.getElementById("a");
+ const b = document.getElementById("b");
+
+ let mouseup_promise = getEvent("mouseup", document);
+
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: a})
+ .pointerDown()
+ .addTick()
+ .addTick()
+ .pointerMove(0, 0, {origin: b})
+ .addTick()
+ .addTick()
+ .pointerUp()
+ .send();
+
+ await mouseup_promise;
+
+ const expected_events = ["mousemove", "mousedown", "selectionchange",
+ "mousemove", "selectionchange"];
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "received events");
+ }, "selectionchange event firing when mousemove event is prevented");
+
+ promise_test(async () => {
+ event_log = [];
+
+ const b = document.getElementById("b");
+ const c = document.getElementById("c");
+
+ let dragstart_promise = getEvent("dragstart", document);
+
+ // A mouseup event is not expected. This avoids timing out when the
+ // dragstart event is missing.
+ let mouseup_promise = getEvent("mouseup", document);
+
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: c})
+ .pointerDown()
+ .addTick()
+ .addTick()
+ .pointerMove(0, 0, {origin: b})
+ .addTick()
+ .addTick()
+ .pointerUp()
+ .send();
+
+ await Promise.race([dragstart_promise, mouseup_promise]);
+
+ const expected_events = ["mousemove", "mousedown", "mousemove", "dragstart"];
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "received events");
+ }, "dragstart event firing when mousemove event is prevented");
+</script>
diff --git a/testing/web-platform/tests/uievents/mouse/resources/mouse-event-reporter-subframe.html b/testing/web-platform/tests/uievents/mouse/resources/mouse-event-reporter-subframe.html
new file mode 100644
index 0000000000..f12f429491
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/resources/mouse-event-reporter-subframe.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<style>
+ body, html {
+ margin: 0;
+ padding; 0;
+ background-color: #bfb;
+ }
+</style>
+<body>
+ <div>Child frame</div>
+</body>
+<script>
+ "use strict";
+ let cancel_mousedown = false;
+
+ window.addEventListener("load", () => {
+ window.addEventListener("message", event => {
+ let data = event.data;
+ if (data.type == "cancel-mousedown")
+ cancel_mousedown = data.param;
+ });
+
+ window.addEventListener("mousedown", e => {
+ let msg = "not-canceled";
+ if (cancel_mousedown) {
+ e.preventDefault();
+ msg = "canceled";
+ }
+ sendMessage(window.top, "mousedown", msg);
+ });
+
+ window.addEventListener("mouseup", e => sendMessage(window.top, "mouseup"));
+
+ sendMessage(parent, "load");
+ });
+</script>
diff --git a/testing/web-platform/tests/uievents/mouse/resources/utils.js b/testing/web-platform/tests/uievents/mouse/resources/utils.js
new file mode 100644
index 0000000000..669a2e2edd
--- /dev/null
+++ b/testing/web-platform/tests/uievents/mouse/resources/utils.js
@@ -0,0 +1,27 @@
+// Sends to Window |w| the object |{type, param}|.
+function sendMessage(w, type, param) {
+ w.postMessage({"type": type, "param": param}, "*");
+}
+
+// Returns a |Promise| that gets resolved with the event object when |target|
+// receives an event of type |event_type|.
+function getEvent(event_type, target) {
+ return new Promise(resolve => {
+ target.addEventListener(event_type, e => resolve(e), {once: true});
+ });
+}
+
+// Returns a |Promise| that gets resolved with |event.data| when |window|
+// receives from |source| a "message" event whose |event.data.type| matches the string
+// |message_data_type|.
+function getMessageData(message_data_type, source) {
+ return new Promise(resolve => {
+ function waitAndRemove(e) {
+ if (e.source != source || !e.data || e.data.type != message_data_type)
+ return;
+ window.removeEventListener("message", waitAndRemove);
+ resolve(e.data);
+ }
+ window.addEventListener("message", waitAndRemove);
+ });
+}