summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/pointerevents
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/pointerevents
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/pointerevents')
-rw-r--r--testing/web-platform/tests/pointerevents/META.yml6
-rw-r--r--testing/web-platform/tests/pointerevents/README.md9
-rw-r--r--testing/web-platform/tests/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html80
-rw-r--r--testing/web-platform/tests/pointerevents/coalesced_events_attributes.https.html130
-rw-r--r--testing/web-platform/tests/pointerevents/coalesced_events_attributes_under_load.https.html93
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html111
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-on-object.html117
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-on-scrollbar.html68
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault-passive.html84
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html117
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-updown-events.html87
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_mouseevent_key_pressed.html60
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action-verification.html45
-rw-r--r--testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html124
-rw-r--r--testing/web-platform/tests/pointerevents/html/pointerevent_drag_interaction-manual.html103
-rw-r--r--testing/web-platform/tests/pointerevents/idlharness.https.window.js21
-rw-r--r--testing/web-platform/tests/pointerevents/inheritance.html21
-rw-r--r--testing/web-platform/tests/pointerevents/mouse-pointer-boundary-events-for-shadowdom.html74
-rw-r--r--testing/web-platform/tests/pointerevents/parsing/touch-action-computed.html24
-rw-r--r--testing/web-platform/tests/pointerevents/parsing/touch-action-invalid.html18
-rw-r--r--testing/web-platform/tests/pointerevents/parsing/touch-action-valid.html23
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-in-iframe.html57
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-scrollbar.html53
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-in-iframe.html58
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-scrollbar.html54
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change.html47
-rw-r--r--testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll.html46
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html223
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html200
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html145
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html128
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_attributes.html301
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_auxclick_is_a_pointerevent.html74
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers.html95
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_boundary_events_in_capturing.html98
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_bubble_ancestors_none.html34
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_bubble_display_none.html40
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_bubble_mousedown_mouseup_different_target.html50
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_capture_mouse.html169
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_capture_suppressing_mouse.html239
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html147
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_click_during_capture.html138
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent.html123
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html98
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_constructor.html97
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_constructor.https.html118
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html77
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_disabled_form_control.html98
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture.html149
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture_release_pending_capture.html88
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_fractional_coordinates.html156
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll.html59
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll_visible_descendant.html59
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_iframe-touch-action-none_touch.html66
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html120
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html129
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_shadow_host.html138
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_is_first.html151
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_remove_setcapture_node.html97
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_mouse_capture_change_hover.html170
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html82
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_movementxy.html152
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_multiple_primary_pointers_boundary_events-manual.html145
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_on_event_handlers.html67
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerId_scope.html99
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html149
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointercancel_touch.html103
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-custom-element.html125
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-shadow-dom.html117
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointercapture-not-lost-in-chorded-buttons.html225
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointercapture_in_frame.html252
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerenter_does_not_bubble.html102
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html72
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendant_over.html77
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendants.html63
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerleave_does_not_bubble.html90
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerleave_pen-manual.html72
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointermove.html50
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointermove_after_pointerup_target_removed.html96
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html85
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointermove_on_chorded_mouse_button.html90
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerout_after_pointercancel_touch.html73
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerout_no_pointer_movement.html85
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerout_pen.html67
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerout_received_once.html71
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.html79
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.https.html69
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_events_to_original_target.html151
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_invalid_pointerid.html86
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch.html87
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointerup_mouse.html100
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_mouse.html103
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_touch.html102
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_release_right_after_capture.html76
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_root_computed_style.html20
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_root_hit_test.html20
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_click.html100
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html97
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_disconnected.html69
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html71
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_invalid_pointerid.html71
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_override_pending_capture_element.html78
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_mouse.html99
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_touch.html102
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_relatedtarget.html124
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_to_same_element_twice.html77
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_styles.css112
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_support.js544
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_click.html122
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html139
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_tiltX_tiltY_to_azimuth_altitude.html109
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-auto-css_touch.html141
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-button-none-test_touch.html88
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-illegal.html67
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html133
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html127
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html128
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html131
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch.html145
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html97
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-keyboard.html149
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-modified_touch.html60
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-mouse.html146
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-css_touch.html126
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-on-body-when-style-propagates-to-viewport_touch.html90
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-down-css_touch.html131
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-left-css_touch.html131
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-right-css_touch.html132
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-up-css_touch.html131
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-css_touch.html121
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html126
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch.html138
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-y-css_touch.html122
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html92
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-span-none-test_touch.html86
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-svg-none-test_touch.html83
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-table-none-test_touch.html139
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-action-verification.html90
-rw-r--r--testing/web-platform/tests/pointerevents/pointerevent_touch-adjustment_click_target.html67
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html107
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.https.html74
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getPredictedEvents_when_pointerlocked-manual.html76
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html123
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html94
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html112
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html149
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html133
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerrawupdate_in_pointerlock.https.html108
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html8
-rw-r--r--testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_pointermove_in_pointerlock-iframe.html11
-rw-r--r--testing/web-platform/tests/pointerevents/pointerup_after_pointerdown_target_removed.html61
-rw-r--r--testing/web-platform/tests/pointerevents/pointerup_button_value_matches_corresponding_pointerdown.html34
-rw-r--r--testing/web-platform/tests/pointerevents/predicted_events_attributes.html124
-rw-r--r--testing/web-platform/tests/pointerevents/resources/iframe-touch-action-none-subframe.html26
-rw-r--r--testing/web-platform/tests/pointerevents/resources/minimal.html4
-rw-r--r--testing/web-platform/tests/pointerevents/resources/pointerevent_fractional_coordinates-iframe.html32
-rw-r--r--testing/web-platform/tests/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html3
-rw-r--r--testing/web-platform/tests/pointerevents/resources/pointerevent_pointerId_scope-iframe.html35
-rw-r--r--testing/web-platform/tests/pointerevents/resources/pointerevent_pointercapture-iframe.html14
-rw-r--r--testing/web-platform/tests/pointerevents/resources/pointerevent_pointerrawupdate_in_pointerlock-iframe.html10
-rw-r--r--testing/web-platform/tests/pointerevents/touch-action-with-swipe-dir-change.html115
161 files changed, 15890 insertions, 0 deletions
diff --git a/testing/web-platform/tests/pointerevents/META.yml b/testing/web-platform/tests/pointerevents/META.yml
new file mode 100644
index 0000000000..e6dbf9deec
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/META.yml
@@ -0,0 +1,6 @@
+spec: https://w3c.github.io/pointerevents/
+suggested_reviewers:
+ - patrickhlauke
+ - flackr
+ - mustaqahmed
+ - smaug----
diff --git a/testing/web-platform/tests/pointerevents/README.md b/testing/web-platform/tests/pointerevents/README.md
new file mode 100644
index 0000000000..0cc0190979
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/README.md
@@ -0,0 +1,9 @@
+Directory for Pointer Events Tests
+
+Latest Editor's Draft: https://w3c.github.io/pointerevents/
+
+Latest W3C Technical Report: http://www.w3.org/TR/pointerevents/
+
+Discussion forum for tests: http://lists.w3.org/Archives/Public/public-test-infra/
+
+Test Assertion table: https://www.w3.org/wiki/PointerEvents/TestAssertions
diff --git a/testing/web-platform/tests/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html b/testing/web-platform/tests/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html
new file mode 100644
index 0000000000..7500277f22
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<title>Capturing boundary event handler at UA Shadow DOM</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ .target {
+ width: 100px;
+ height: 50px;
+ border: 1px solid red;
+ }
+</style>
+<!-- This div has no shadow dom, serves only as a "control" group. -->
+<div class="target"></div>
+<video class="target"></video>
+<input class="target"></input>
+<div id="done">done</div>
+
+<script>
+ "use strict";
+ const pointer_type = location.search.substring(1);
+
+ const targets = document.getElementsByClassName("target");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ function logEventAndPhase(e) {
+ event_log.push(`${e.type}-${e.eventPhase}`);
+ }
+
+ const logged_events = [
+ "pointerenter", "pointerleave", "pointerover", "pointerout"
+ ];
+ const expected_events_and_phases = [
+ "pointerover-2", "pointerenter-2", "pointerout-2", "pointerleave-2"
+ ];
+
+ function addPromiseTest(target) {
+ const test_name = `Capturing boundary event handler at ${target.tagName}`;
+ promise_test(async () => {
+ event_log = [];
+
+ logged_events.forEach(ename => {
+ target.addEventListener(ename, logEventAndPhase, {capture:true});
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ logged_events.forEach(ename => {
+ target.removeEventListener(ename, logEventAndPhase, {capture:true});
+ });
+
+ assert_equals(event_log.toString(),
+ expected_events_and_phases.toString(),
+ "received events with phases");
+ }, test_name);
+ }
+
+ for (let i = 0; i < targets.length; i++)
+ addPromiseTest(targets.item(i));
+</script>
diff --git a/testing/web-platform/tests/pointerevents/coalesced_events_attributes.https.html b/testing/web-platform/tests/pointerevents/coalesced_events_attributes.https.html
new file mode 100644
index 0000000000..f6026048b6
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/coalesced_events_attributes.https.html
@@ -0,0 +1,130 @@
+<!doctype html>
+<title>Coalesced events count and properties</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<meta name="variant" content="?touch">
+<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>
+<script src="pointerevent_support.js"></script>
+<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="target"></div>
+<div id="done"></div>
+
+<script>
+ "use strict";
+ const pointer_type = location.search.substring(1);
+ const target = document.getElementById("target");
+
+ // https://w3c.github.io/pointerevents/#coalesced-events
+ function checkListAttributes(event) {
+ assert_equals(typeof event.getCoalescedEvents, "function",
+ event.type + ".getCoalescedEvents is a function");
+ assert_equals(typeof event.getCoalescedEvents(), "object",
+ event.type + ".getCoalescedEvents() returns an object");
+ if (event.type == "pointermove") {
+ assert_greater_than_equal(event.getCoalescedEvents().length, 1,
+ event.type + ".getCoalescedEvents() has at least 1 entry");
+ } else {
+ assert_equals(event.getCoalescedEvents().length, 0,
+ event.type + ".getCoalescedEvents() has 0 entry");
+ }
+ }
+
+ promise_test(async () => {
+ const done = document.getElementById("done");
+
+ let pointerover_promise = getEvent("pointerover", target);
+ let pointerenter_promise = getEvent("pointerenter", target);
+ let pointerout_promise = getEvent("pointerout", target);
+ let pointerleave_promise = getEvent("pointerleave", target);
+
+ await clickInTarget(pointer_type, target);
+ await clickInTarget(pointer_type, done);
+
+ checkListAttributes(await pointerover_promise);
+ checkListAttributes(await pointerenter_promise);
+ checkListAttributes(await pointerout_promise);
+ checkListAttributes(await pointerleave_promise);
+ }, "Coalesced list in boundary events");
+
+ promise_test(async () => {
+ // We need "touch-action:none" to guarantee pointermove events.
+ target.classList.add("touchActionNone");
+
+ target.addEventListener("pointerdown",
+ e => target.setPointerCapture(e.pointerId),
+ {once: true});
+
+ target.addEventListener("pointermove",
+ e => target.releasePointerCapture(e.pointerId),
+ {once: true});
+
+ let gotpointercapture_promise = getEvent("gotpointercapture", target);
+ let lostpointercapture_promise = getEvent("lostpointercapture", target);
+
+ await new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(20, 20, {origin: target})
+ .pointerUp()
+ .send();
+
+ checkListAttributes(await gotpointercapture_promise);
+ checkListAttributes(await lostpointercapture_promise);
+
+ target.classList.remove("touchActionNone");
+ }, "Coalesced list in pointer-capture events");
+
+ promise_test(async () => {
+ // We need "touch-action:none" to guarantee pointermove events.
+ target.classList.add("touchActionNone");
+
+ let pointerdown_promise = getEvent("pointerdown", target);
+ let pointermove_promise = getEvent("pointermove", target);
+ let pointerup_promise = getEvent("pointerup", target);
+
+ await new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(20, 20, {origin: target})
+ .pointerUp()
+ .send();
+
+ checkListAttributes(await pointerdown_promise);
+ checkListAttributes(await pointermove_promise);
+ checkListAttributes(await pointerup_promise);
+
+ target.classList.remove("touchActionNone");
+ }, "Coalesced list in pointerdown/move/up events");
+
+ promise_test(async () => {
+ if (pointer_type !== "touch") {
+ assert_true(true, "Skipped for " + pointer_type);
+ return;
+ }
+
+ let pointercancel_promise = getEvent("pointercancel", target);
+
+ await new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(20, 20, {origin: target})
+ .pointerUp()
+ .send();
+
+ checkListAttributes(await pointercancel_promise);
+ }, "Coalesced list in pointercancel event");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/coalesced_events_attributes_under_load.https.html b/testing/web-platform/tests/pointerevents/coalesced_events_attributes_under_load.https.html
new file mode 100644
index 0000000000..262742558e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/coalesced_events_attributes_under_load.https.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<title>Coalesced events under load</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<meta name="variant" content="?touch">
+<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>
+<script src="pointerevent_support.js"></script>
+<style>
+ #target {
+ width: 100px;
+ height: 100px;
+ touch-action: none;
+ }
+</style>
+<div id="target"></div>
+
+<script>
+ "use strict";
+ const pointer_type = location.search.substring(1);
+ const target = document.getElementById("target");
+
+ // https://w3c.github.io/pointerevents/#coalesced-events
+ function checkCoalescedMoveEventAttributes(event) {
+ let coalesced_events = event.getCoalescedEvents();
+ assert_greater_than_equal(coalesced_events.length, 1,
+ "pointermove.getCoalescedEvents() has at least 1 entry");
+
+ for (let i = 0; i < coalesced_events.length; i++) {
+ let coalesced_event = coalesced_events[i];
+
+ assert_equals(coalesced_event.isTrusted, true,
+ "coalesced_event.isTrusted is true");
+ assert_equals(coalesced_event.bubbles, false,
+ "coalesced_event.bubbles is false");
+ assert_equals(coalesced_event.cancelable, false,
+ "coalesced_event.cancelable is false");
+
+ if (i > 0) {
+ assert_greater_than_equal(coalesced_event.timeStamp,
+ coalesced_events[i-1].timeStamp,
+ "coalesced_event.timeStamp must be ascending");
+ }
+ }
+ }
+
+ let coalesced_event_received = false;
+
+ promise_test(async t => {
+ let current_busyloop_ms = 5;
+
+ target.addEventListener("pointerdown", event => {
+ // Every pointerdown blocks the main thread for a certain time limit,
+ // and then increases the time limit for next round in case the
+ // current limit fails to cause event coalescing.
+ let start = performance.now();
+ while (performance.now() < start + current_busyloop_ms)
+ continue;
+ current_busyloop_ms *= 2;
+ });
+
+ target.addEventListener("pointermove", t.step_func(event => {
+ checkCoalescedMoveEventAttributes(event);
+ if (event.getCoalescedEvents().length > 1)
+ coalesced_event_received = true;
+ }));
+
+ // Repeatedly send a long action sequence until either a coalesced event is
+ // encountered or the busyloop becomes too long.
+ while (!coalesced_event_received && current_busyloop_ms < 500) {
+ let pointerup_promise = getEvent("pointerup", target);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, { origin: target })
+ .pointerDown();
+ for (let i = 0; i < 5; i++) {
+ actions = actions.pointerMove(20, 20, { origin: target })
+ .pointerMove(0, 0, { origin: target });
+ }
+ actions = actions.pointerUp();
+
+ await actions.send();
+ await pointerup_promise;
+ }
+
+ assert_true(coalesced_event_received, "Coalesed pointermoves received");
+ }, "Coalesced pointermoves under load");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html
new file mode 100644
index 0000000000..6d13b0de35
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html
@@ -0,0 +1,111 @@
+<!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 type="text/javascript" src="../pointerevent_support.js"></script>
+<!-- There are significant differences in how browsers fire pointer events and
+compat mouse events when elements are removed from the dom.
+There is a discussion about the order of pointer events and mouse compat events
+for this scenario in the pointer event working group (See link below)-->
+<link rel="help" href="https://github.com/w3c/pointerevents/issues/285">
+<style>
+div.box {
+ margin: 5px;
+ padding: 20px;
+ float: left;
+ background-color:green;
+}
+#grey {
+ width: 50px;
+ height: 50px;
+}
+</style>
+
+<h1>Verifies the compatibility mouse events are sent correctly when the node is deleted on pointer event handler.</h1>
+
+<div id="grey" class="box" style="background-color:grey">
+</div>
+<button id="done">Done</button>
+
+<div id="console"></div>
+
+<script>
+
+var receivedEvents = [];
+var done_clicked = false;
+
+var eventList = ["mousedown", "mouseup", "mousemove",
+ "pointerdown", "pointerup", "pointermove"];
+
+
+var removeNodeEvent = "";
+
+var greyDiv = document.getElementById("grey");
+eventList.forEach(function(eventName) {
+ greyDiv.addEventListener(eventName, function(event) {
+ if (event.eventPhase == Event.AT_TARGET) {
+ receivedEvents.push(event.type+"@grey");
+ }
+ });
+});
+
+function createGreenBoxAndAddListeners() {
+ var greenDiv = document.createElement("div");
+ greenDiv.setAttribute("class", "box");
+ greenDiv.id = "green";
+ greyDiv.innerHTML = "";
+ greyDiv.appendChild(greenDiv);
+ eventList.forEach(function(eventName) {
+ greenDiv.addEventListener(eventName, function(event) {
+ if (event.eventPhase == Event.AT_TARGET) {
+ receivedEvents.push(event.type+"@green");
+ if (event.type == removeNodeEvent) {
+ greenDiv.parentNode.removeChild(greenDiv);
+ }
+ }
+ });
+ });
+}
+
+var done_button = document.getElementById("done");
+done_button.addEventListener("click",()=>done_clicked=true);
+
+function performActions() {
+ var rect = document.getElementById("green").getBoundingClientRect();
+ var x1 = Math.ceil(rect.left + 5);
+ var y1 = Math.ceil(rect.top + 5);
+
+ return new test_driver.Actions()
+ .pointerMove(0, 0)
+ .pointerMove(x1, y1)
+ .pointerDown()
+ .pointerUp()
+ .send()
+ .then(()=> test_driver.click(done_button));
+}
+
+function testScenario(targetEvent, expectedEvents, description) {
+ promise_test(async () => {
+ receivedEvents = [];
+ removeNodeEvent = targetEvent;
+ createGreenBoxAndAddListeners();
+
+ await performActions();
+ assert_array_equals(receivedEvents, expectedEvents);
+ }, description);
+}
+
+testScenario("", ["pointermove@green", "mousemove@green", "pointerdown@green", "mousedown@green", "pointerup@green", "mouseup@green"],
+ "Compat mouse events with no node removal");
+
+testScenario("pointermove", ["pointermove@green", "mousemove@grey", "pointerdown@grey", "mousedown@grey", "pointerup@grey", "mouseup@grey"],
+ "Compat mouse events with node removal on pointermove");
+
+testScenario("pointerdown", ["pointermove@green", "mousemove@green", "pointerdown@green", "mousedown@grey", "pointerup@grey", "mouseup@grey"],
+ "Compat mouse events with node removal on pointerdown");
+
+testScenario("pointerup", ["pointermove@green", "mousemove@green", "pointerdown@green", "mousedown@green", "pointerup@green", "mouseup@grey"],
+ "Compat mouse events with node removal on pointerup");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-on-object.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-on-object.html
new file mode 100644
index 0000000000..27142a4f59
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-on-object.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<script type="text/javascript" src="/resources/testdriver.js"></script>
+<script type="text/javascript" src="/resources/testdriver-vendor.js"></script>
+<script type="text/javascript" src="/resources/testdriver-actions.js"></script>
+<script type="text/javascript" src="../pointerevent_support.js"></script>
+
+<style>
+#obj {
+ width: 50px;
+ height: 50px;
+ padding: 50px;
+ background-color: lightblue;
+ border:1px solid black;
+}
+div {
+ display: block;
+}
+</style>
+
+<h1>Verifies that mouse activities on an object fire pointerevents. It expected to get pointerup when the pointerdown happened on the object for compatibility with flash objects.</h1>
+
+
+<p>
+ To test that when clicking inside the blue rectangle all compat mouse events are correct:
+ <ul>
+ <li> Click once in the blue rectangle
+ <li> Click the Done button
+ </ul>
+</p>
+<p>
+ To test that when dragging mouse outside all compat mouse events are correct:
+ <ul>
+ <li> Press left mouse button in the blue rectangle
+ <li> Drag the mouse cursor out of the blue rectangle
+ <li> Release the left mouse button
+ <li> Click the Done button
+ </ul>
+</p>
+<!-- draggable is set to false because there is a difference between auto draggable value in different browsers -->
+<object id="obj" draggable="false"></object>
+<button id="done">Done</button>
+<div id="log"></div>
+<script>
+var target = document.getElementById("obj");
+var done = document.getElementById("done");
+
+var rect = target.getBoundingClientRect();
+var done_clicked = 0;
+var receivedEvents = [];
+var previous_done_clicked = 0;
+
+["mousedown", "mouseup", "mousemove", "pointerdown", "pointerup", "pointermove"].forEach(function(eventName) {
+ target.addEventListener(eventName, function(event) {
+ // This will clear receivedEvents once another test starts
+ if(previous_done_clicked !== done_clicked){
+ previous_done_clicked = done_clicked;
+ receivedEvents = [];
+ }
+ receivedEvents.push(event.type);
+ });
+});
+
+// Simple test to check that passive listeners can't prevent compatibility events.
+target.addEventListener("pointerdown", function(event) {
+ event.preventDefault();
+ },
+ { passive: true, once: true });
+
+document.getElementById('done').addEventListener('click', (e) => done_clicked++);
+
+// Need to prevent the default behaviour for firefox
+target.addEventListener("dragstart", (e)=>e.preventDefault());
+
+if(window.promise_test){
+ promise_test(async() => {
+ receivedEvents = [];
+
+ await new test_driver.Actions()
+ .pointerMove(Math.ceil(rect.left+5), Math.ceil(rect.top+5))
+ .pointerDown()
+ .pointerUp()
+ .send()
+ .then(() => clickInTarget("mouse", done));
+ await resolveWhen(() => done_clicked === 1);
+
+ assert_array_equals(receivedEvents.filter(isPointerEvent), ["pointermove", "pointerdown", "pointerup"],
+ "Click on object should result in the correct sequence of pointer events");
+ assert_array_equals(receivedEvents.filter(isMouseEvent), ["mousemove", "mousedown", "mouseup"],
+ "Click on object should result in the correct sequence of mouse events");
+ assert_true(arePointerEventsBeforeCompatMouseEvents(receivedEvents),
+ "Click on object should result in the correct sequence of events: " + receivedEvents);
+ }, "Normal click event sequence within object");
+
+ promise_test(async() => {
+ receivedEvents = [];
+
+ await new test_driver.Actions()
+ .pointerMove(Math.ceil(rect.left+5), Math.ceil(rect.top+5))
+ .pointerDown()
+ .pointerMove(Math.ceil(rect.left-5), Math.ceil(rect.top-5))
+ .pointerUp()
+ .send()
+ .then(() => clickInTarget("mouse", done));
+ await resolveWhen(() => done_clicked === 2);
+
+ assert_array_equals(receivedEvents.filter(isPointerEvent), ["pointermove", "pointerdown", "pointermove", "pointerup"],
+ "Drag from object should result in the correct sequence of pointer events");
+ assert_array_equals(receivedEvents.filter(isMouseEvent), ["mousemove", "mousedown", "mousemove", "mouseup"],
+ "Drag from object should result in the correct sequence of mouse events");
+ assert_true(arePointerEventsBeforeCompatMouseEvents(receivedEvents),
+ "Drag from object should result in the correct sequence of events: " + receivedEvents);
+
+ }, "Click and drag outside of object event sequence");
+}
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-on-scrollbar.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-on-scrollbar.html
new file mode 100644
index 0000000000..43d78e147e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-on-scrollbar.html
@@ -0,0 +1,68 @@
+<!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 type="text/javascript" src="../pointerevent_support.js"></script>
+<style>
+#target {
+ height: 100px;
+ width: 100px;
+ overflow-y: scroll;
+}
+#spacer {
+ background: green;
+ height: 200px;
+}
+</style>
+<h1>Verifies that pointerup/down are fired correctly for corresponding mouse events on the scollbar.</h1>
+<div id="target">
+<div id="spacer"></div>
+</div>
+
+<div id="console"></div>
+
+<script>
+var receivedEvents = [];
+var targetDiv = document.getElementById('target');
+
+function init() {
+ var eventList = ["mousedown", "mouseup", "pointerdown", "pointerup"];
+ eventList.forEach(function(eventName) {
+ targetDiv.addEventListener(eventName, function(event) {
+ receivedEvents.push(event.type + "@target");
+ });
+ });
+}
+
+function performActions(x, y){
+ return new test_driver.Actions()
+ .pointerMove(0, 0)
+ .pointerMove(x, y)
+ .pointerDown(0)
+ .pointerUp(0)
+ .send()
+ .then(()=>resolveWhen(()=>receivedEvents.length == 4));
+}
+
+function runTests() {
+ var rect = targetDiv.getBoundingClientRect();
+ var x1 = rect.right - 5;
+ var y1 = rect.top + 20;
+
+ test(function(){
+ assert_equals(targetDiv, document.elementFromPoint(x1,y1),
+ "Didn't hit the scrollbar as expected");
+ }, `Test point (${x1},${y1}) is on the scrollbar`);
+
+ promise_test(async () => {
+ await performActions(Math.ceil(x1), Math.ceil(y1));
+ assert_array_equals(receivedEvents, ["pointerdown@target", "mousedown@target",
+ "pointerup@target", "mouseup@target"]);
+ }, "Verifies that pointerup/down are fired correctly for corresponding mouse events on the scollbar.");
+}
+
+init();
+runTests();
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault-passive.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault-passive.html
new file mode 100644
index 0000000000..57da096866
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault-passive.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<title>Canceling passive pointerevents does not affect compat mouseevents</title>
+<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="../pointerevent_support.js"></script>
+<style>
+ div {
+ margin: 20px;
+ padding: 20px;
+ background-color: green;
+ user-select: none; // Prevents text selection on drag.
+ }
+</style>
+<div id="logger" draggable="false"></div>
+<div id="done"></div>
+
+<script>
+ 'use strict';
+
+ const logger = document.getElementById("logger");
+ const done = document.getElementById("done");
+
+ let received_events = [];
+
+ const logged_pointer_events = ["pointerdown", "pointermove", "pointerup",
+ "pointerenter", "pointerleave", "pointerover", "pointerout"];
+ const logged_mouse_events = ["mousedown", "mousemove", "mouseup",
+ "mouseenter", "mouseleave", "mouseover", "mouseout"];
+
+ const expected_pointer_events = [
+ "pointerover", "pointerenter",
+ "pointermove", "pointerdown", "pointermove", "pointerup",
+ "pointerout", "pointerleave"
+ ];
+
+ const expected_mouse_events = [
+ "mouseover", "mouseenter",
+ "mousemove", "mousedown", "mousemove", "mouseup",
+ "mouseout", "mouseleave"
+ ];
+
+ logged_pointer_events.forEach(ename => logger.addEventListener(ename, e => {
+ received_events.push(e.type);
+ e.preventDefault();
+ }, { passive: true }));
+
+ logged_mouse_events.forEach(ename => logger.addEventListener(ename, e =>
+ received_events.push(e.type)));
+
+ promise_test(async () => {
+ received_events = [];
+
+ let click_on_done = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ // Start outside all event listeners
+ .pointerMove(0, 0)
+ .pointerDown()
+ .pointerUp()
+ // Drag within "logger"
+ .pointerMove(0, 0, { origin: logger })
+ .pointerDown()
+ .pointerMove(15, 15, { origin: logger })
+ .pointerUp()
+ // Click "done"
+ .pointerMove(0, 0, { origin: done })
+ .pointerDown()
+ .pointerUp()
+ .send();
+
+ await actions;
+ await click_on_done;
+
+ assert_array_equals(received_events.filter(isPointerEvent),
+ expected_pointer_events, "expected pointer events");
+ assert_array_equals(received_events.filter(isMouseEvent),
+ expected_mouse_events, "expected mouse events");
+ assert_true(arePointerEventsBeforeCompatMouseEvents(received_events),
+ "pairing of pointer/mouse events");
+ }, "Canceling passive pointerevents does not affect compat mouseevents");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html
new file mode 100644
index 0000000000..977c4e90ca
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<title>Effect of pointer event prevent-default on compat mouse event</title>
+<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="../pointerevent_support.js"></script>
+<style>
+ div {
+ margin: 20px;
+ padding: 20px;
+ background-color: green;
+ user-select: none; // Prevents text selection on drag.
+ }
+
+</style>
+<div id="preventer" draggable="false">
+ <div id="logger" draggable="false"></div>
+ <!-- The following div makes the center of "preventer" outside "logger", which
+ simplifies TestDriver action coordinates below. -->
+ <div></div>
+</div>
+<div id="done"></div>
+
+<script>
+ 'use strict';
+
+ const logger = document.getElementById("logger");
+ const preventer = document.getElementById("preventer");
+ const done = document.getElementById("done");
+
+ const preventing_handler = e => e.preventDefault();
+
+ let received_events = [];
+
+ const logged_pointer_events = ["pointerdown", "pointermove", "pointerup",
+ "pointerenter", "pointerleave", "pointerover", "pointerout"];
+ const logged_mouse_events = ["mousedown", "mousemove", "mouseup",
+ "mouseenter", "mouseleave", "mouseover", "mouseout"];
+
+ const expected_pointer_events = [
+ "pointerover", "pointerenter", "pointermove", "pointerdown",
+ "pointerout", "pointerleave", "pointerover", "pointerenter",
+ "pointermove", "pointerup", "pointerout", "pointerleave"
+ ];
+
+ const expected_mouse_events_when_unaffected_by_pointer_events = [
+ "mouseover", "mouseenter", "mousemove", "mousedown",
+ "mouseout", "mouseleave", "mouseover", "mouseenter",
+ "mousemove", "mouseup", "mouseout", "mouseleave"
+ ];
+
+ const expected_mouse_events_for_canceled_pointerdown = [
+ "mouseover", "mouseenter", "mousemove",
+ "mouseout", "mouseleave", "mouseover", "mouseenter",
+ "mouseout", "mouseleave"
+ ];
+
+ logged_pointer_events.forEach(ename =>
+ logger.addEventListener(ename, e => received_events.push(e.type)));
+
+ logged_mouse_events.forEach(ename =>
+ logger.addEventListener(ename, e => received_events.push(e.type)));
+
+ for (let i = 0; i < logged_pointer_events.length; i++) {
+ let event_to_cancel = logged_pointer_events[i];
+
+ promise_test(async (test) => {
+ received_events = [];
+
+ preventer.addEventListener(event_to_cancel, preventing_handler);
+ test.add_cleanup(() =>
+ preventer.removeEventListener(event_to_cancel, preventing_handler));
+
+ let click_on_done = getEvent("click", done, test);
+
+ let actions = new test_driver.Actions()
+ // Start outside all event listeners
+ .pointerMove(0, 0)
+ .pointerDown()
+ .pointerUp()
+ // Drag from inside to outside of "logger" then drag back to inside,
+ // staying within "preventer" all the time
+ .pointerMove(0, 0, { origin: logger })
+ .pointerDown()
+ .pointerMove(0, 0, { origin: preventer })
+ .pointerUp()
+ .pointerDown()
+ .pointerMove(0, 0, { origin: logger })
+ .pointerUp()
+ // Click "done"
+ .pointerMove(0, 0, { origin: done })
+ .pointerDown()
+ .pointerUp()
+ .send();
+
+ await actions;
+ await click_on_done;
+
+ assert_array_equals(received_events.filter(isPointerEvent),
+ expected_pointer_events, "expected pointer events");
+
+ if (event_to_cancel == "pointerdown") {
+ assert_array_equals(received_events.filter(isMouseEvent),
+ expected_mouse_events_for_canceled_pointerdown,
+ "expected mouse events");
+ } else {
+ assert_array_equals(received_events.filter(isMouseEvent),
+ expected_mouse_events_when_unaffected_by_pointer_events,
+ "expected mouse events");
+ assert_true(arePointerEventsBeforeCompatMouseEvents(received_events),
+ "compat mouse events follow corresponding pointer events");
+ }
+ }, `Effect of canceling ${event_to_cancel} on compat mouse events`);
+ }
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-updown-events.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-updown-events.html
new file mode 100644
index 0000000000..a5433aab53
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouse-pointer-updown-events.html
@@ -0,0 +1,87 @@
+<!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 type="text/javascript" src="../pointerevent_support.js"></script>
+<style>
+div.box {
+ margin: 10px;
+ padding: 50px;
+ float: left;
+}
+</style>
+<h1>Verifies that pointerup/down are fired correctly for corresponding mouse events</h1>
+<div id="lightgreen" class="box" style="background-color:lightgreen">
+ <div id="green" class="box" style="background-color:green">
+ </div>
+</div>
+
+<div id="console"></div>
+
+<script>
+var eventsReceived = [];
+function init() {
+ var eventList = ["mousedown", "mouseup", "pointerdown", "pointerup"];
+
+ ["green", "lightgreen"].forEach(function(id) {
+ var targetDiv = document.getElementById(id);
+
+ eventList.forEach(function(eventName) {
+ targetDiv.addEventListener(eventName, function(event) {
+ eventsReceived.push(`${event.type}@${id}`);
+ });
+ });
+ });
+}
+
+function performActions() {
+ var rect = document.getElementById("green").getBoundingClientRect();
+ var x1 = Math.ceil(rect.left + 5);
+ var y1 = Math.ceil(rect.top + 5);
+
+ var rect = document.getElementById("lightgreen").getBoundingClientRect();
+ var x2 = Math.ceil(rect.left + 5);
+ var y2 = Math.ceil(rect.top + 5);
+
+ return new test_driver.Actions()
+ .pointerMove(x1, y1)
+ .pointerDown()
+ .pointerUp()
+ .pointerDown()
+
+ .pointerMove(x2, y2)
+ .pointerUp()
+ .pointerDown()
+
+ .pointerMove(x1, y1)
+ .pointerUp()
+ .send()
+ .then(()=>resolveWhen(()=>eventsExpected.length === eventsReceived.length));
+}
+var eventsExpected = ["pointerdown@green", "pointerdown@lightgreen",
+ "mousedown@green", "mousedown@lightgreen",
+ "pointerup@green", "pointerup@lightgreen",
+ "mouseup@green", "mouseup@lightgreen",
+ "pointerdown@green", "pointerdown@lightgreen",
+ "mousedown@green", "mousedown@lightgreen",
+ "pointerup@lightgreen", "mouseup@lightgreen",
+ "pointerdown@lightgreen", "mousedown@lightgreen",
+ "pointerup@green", "pointerup@lightgreen",
+ "mouseup@green", "mouseup@lightgreen"];
+function runTests(){
+ promise_test((test) => {
+ return new Promise(async (resolve)=>{
+ await performActions();
+ test.step(()=>{
+ assert_array_equals(eventsExpected, eventsReceived);
+ });
+ resolve();
+ });
+ }, "pointerup/down events are fired correctly for corresponding mouse events");
+}
+
+init();
+runTests();
+</script>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_mouseevent_key_pressed.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouseevent_key_pressed.html
new file mode 100644
index 0000000000..ea45adee82
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_mouseevent_key_pressed.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Mouse events with keys pressed</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-vendor.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <script>
+ let testMouseKeyPressed = async_test('Tests that the mouse events with some keys pressed.');
+ let activeKeys = false;
+
+ window.addEventListener('pointermove', function(e) {
+ activeKeys = e.getModifierState("Alt") &&
+ e.getModifierState("Control") &&
+ e.getModifierState("Meta") &&
+ e.getModifierState("Shift");
+ });
+
+ async function runTest(){
+ let event_watcher = new EventWatcher(testMouseKeyPressed, window, ["pointermove"],
+ ()=>waitForAnimationFrames(200));
+ await Promise.all([event_watcher.wait_for(["pointermove"]), inject_input()]);
+ testMouseKeyPressed.step_func_done(()=>{
+ assert_true(activeKeys, "Modifier keys not reflected in the pointermove event!");
+ })();
+ }
+
+ function inject_input() {
+ const x = 100;
+ const y = 100;
+ const altKey = '\uE00A';
+ const ctrlKey = '\uE009';
+ const metaKey = '\uE03d';
+ const shiftKey = '\uE008'
+
+ // First press Alt, Control, Meta, Shift keys and then send a mouse move.
+ return new test_driver.Actions()
+ .keyDown(altKey)
+ .keyDown(ctrlKey)
+ .keyDown(metaKey)
+ .keyDown(shiftKey)
+ .pointerMove(x,y)
+ .send();
+ }
+ </script>
+
+ </head>
+ <body id="target" onload="runTest()">
+ <h4>Test Description: Tests that the mouse events with some keys pressed.
+ <ol>
+ <li>Press Alt, Control, Meta, Shift keys and move the mouse</li>
+ </ol>
+ </h4>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action-verification.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action-verification.html
new file mode 100644
index 0000000000..311d24b628
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action-verification.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+ <head>
+<!-- This file tests the additional touch-action values that are not in the main spec
+ but are mostly implemented by browsers. It should be integrated into main test
+ suite when they are accepted into the specification. -->
+ <title>touch-action: basic verification</title>
+ <meta name="assert" content="The touch-action CSS property determines whether touch input MAY trigger default behavior supplied by the user agent.
+ pinch-zoom: The user agent MAY consider touches that begin on the element only for the purposes of zooming in and out.
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Test will automatically check parsing behaviour of various touch-action combinations.</h4>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ setup({ explicit_done: true });
+
+ function run() {
+ var tests = document.querySelectorAll('.test');
+ //TA 15.20
+ for (var i = 0; i < tests.length; i++) {
+ test(function() {
+ var style = window.getComputedStyle(tests[i]);
+ assert_equals(tests[i].attributes.expected.value, style.touchAction);
+ }, tests[i].id);
+ }
+ done();
+ }
+ </script>
+ <h1>touch-action: basic verification</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ <div class="test" id="explicit-pinch-zoom" style="touch-action: pinch-zoom;" expected="pinch-zoom"></div>
+ <div class="test" id="explicit-pinch-zoom-pan-x-pan-up" style="touch-action: pinch-zoom pan-x pan-up;" expected="pan-x pan-up pinch-zoom"></div>
+ <div class="test" id="explicit-pinch-zoom-pan-x-pan-y" style="touch-action: pinch-zoom pan-x pan-y;" expected="manipulation"></div>
+ <div class="test" id="explicit-invalid-14" style="touch-action: pinch-zoom none;" expected="auto"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html b/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
new file mode 100644
index 0000000000..91b0e6a1b4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: touch-action test for two-finger interaction</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <link rel="help" href="https://compat.spec.whatwg.org/#touch-action" />
+ <meta name="assert" content="Tests that a two-finger pan gesture is cancelled in 'touch-action: pan-x pan-y' but is allowed in 'touch-action: pinch-zoom'"/>
+ <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>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var event_log = [];
+ var actions_promise;
+ var down_ids = new Set();
+ var release_ids = new Set();
+
+ function resetTestState() {
+ event_log = [];
+ down_ids = new Set();
+ release_ids = new Set();
+ }
+
+ function run() {
+ var test_pointer_events = [
+ setup_pointerevent_test("two-finger pan on 'touch-action: pan-x pan-y'", ["touch"]),
+ setup_pointerevent_test("two-finger pan on 'touch-action: pinch-zoom'", ["touch"])
+ ];
+ var expected_events = [
+ "pointerdown@black, pointerdown@black, pointerup@black, pointerup@black",
+ "pointerdown@grey, pointerdown@grey, pointercancel@grey, pointercancel@grey"
+ ];
+ var current_test_index = 0;
+ var black = document.getElementById("black");
+ var grey = document.getElementById("grey");
+ var done = document.getElementById("done");
+
+ on_event(done, "click", function() {
+ test_pointer_events[current_test_index].step(function () {
+ assert_equals(down_ids.size, 2);
+ assert_equals(release_ids.size, 2);
+ assert_true([...down_ids].every(value => release_ids.has(value)));
+ assert_equals(event_log.join(", "), expected_events[current_test_index]);
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointer_events[current_test_index++].done();
+ if (current_test_index < 2) {
+ actions_promise = twoFingerDrag(grey).then(function() {
+ return clickInTarget("touch", done);
+ });
+ }
+ });
+ });
+
+ var targets = [black, grey];
+
+ ["pointerdown", "pointerup", "pointercancel"].forEach(function(eventName) {
+ targets.forEach(function(target){
+ on_event(target, eventName, function (event) {
+ event_log.push(event.type + "@" + event.target.id);
+
+ if (event.type == "pointerdown") {
+ down_ids.add(event.pointerId);
+ } else {
+ release_ids.add(event.pointerId);
+ }
+ });
+ });
+ });
+
+ // Inject touch inputs.
+ actions_promise = twoFingerDrag(black).then(function() {
+ return clickInTarget("touch", done);
+ });
+ }
+ </script>
+ <style>
+ .box {
+ width: 250px;
+ height: 150px;
+ float: left;
+ margin: 10px;
+ }
+
+ #black {
+ touch-action: pan-x pan-y;
+ background-color: black;
+ }
+
+ #grey {
+ touch-action: pinch-zoom;
+ background-color: grey;
+ }
+
+ #done {
+ float: left;
+ padding: 20px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: touch-action test for two-finger interaction</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Tests that a two-finger pan gesture is cancelled in 'touch-action: pan-x pan-y' but is allowed in 'touch-action: pinch-zoom'
+ </h4>
+ <ol>
+ <li>Touch on Black with two fingers and drag both fingers down at same speed.</li>
+ <li>Tap on Done.</li>
+ <li>Touch on Grey with two fingers and drag both fingers down at same speed.</li>
+ <li>Tap on Done.</li>
+ </ol>
+ <div class="box" id="black"></div>
+ <input type="button" id="done" value="Done" />
+ <div class="box" id="grey"></div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/html/pointerevent_drag_interaction-manual.html b/testing/web-platform/tests/pointerevents/html/pointerevent_drag_interaction-manual.html
new file mode 100644
index 0000000000..1a80d239b8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/html/pointerevent_drag_interaction-manual.html
@@ -0,0 +1,103 @@
+<html>
+ <head>
+ <title>Pointer Events interaction with drag and drop</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <script>
+ var eventList = ['pointerdown', 'pointerup', 'pointercancel', 'gotpointercapture', 'lostpointercapture', 'dragstart', 'mousedown'];
+
+ PhaseEnum = {
+ DndWithoutCapture: 0,
+ DndWithCapture: 1,
+ DndWithCaptureMouse: 2,
+ DndPrevented: 3,
+ Done: 4,
+ };
+ var phase = PhaseEnum.DndWithoutCapture;
+ var received_events = [];
+ var pointerId = -1;
+
+ function resetTestState() {
+ phase = PhaseEnum.DndWithoutCapture;
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointer events vs drag and drop", ['mouse']);
+
+ var target0 = document.querySelector('#target0');
+ var target1 = document.querySelector('#target1');
+
+ function handleEvent(e) {
+ if (e.type == 'pointerdown') {
+ received_events = [];
+ if (phase == PhaseEnum.DndWithCapture) {
+ target0.setPointerCapture(e.pointerId);
+ } else if (phase == PhaseEnum.DndWithCaptureMouse) {
+ pointerId = e.pointerId;
+ }
+ }
+ if (e.type == 'mousedown') {
+ if (phase == PhaseEnum.DndWithCaptureMouse) {
+ target0.setPointerCapture(pointerId);
+ }
+ }
+ received_events.push(e.type + "@" + e.target.id);
+ if (e.type == 'dragstart') {
+ e.dataTransfer.setData('text/plain', 'dragstart test');
+ if (phase == PhaseEnum.DndPrevented)
+ e.preventDefault();
+ }
+ if (phase == PhaseEnum.DndWithoutCapture && e.type == 'pointercancel') {
+ phase++;
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, dragstart@target0, pointercancel@target0", "Pointercancel should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel when drag operation starts");
+ } else if (phase == PhaseEnum.DndWithCapture && e.type == 'lostpointercapture') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, gotpointercapture@target0, dragstart@target0, pointercancel@target0, lostpointercapture@target0", "Pointercancel and lostpointercapture should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel while capturing when drag operation starts");
+ phase++;
+ } else if (phase == PhaseEnum.DndWithCaptureMouse && e.type == 'lostpointercapture') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, gotpointercapture@target0, dragstart@target0, pointercancel@target0, lostpointercapture@target0", "Pointercancel and lostpointercapture should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel while capturing on mousedown when drag operation starts");
+ phase++;
+ } else if (phase == PhaseEnum.DndPrevented && e.type == 'pointerup') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, dragstart@target0, pointerup@target1", "Pointerevent stream shouldn't get interrupted when drag is prevented.");
+ }, "Pointerevent stream when drag is prevented.");
+ phase++;
+ test_pointerEvent.done();
+ }
+ }
+ eventList.forEach(function(eventName) {
+ on_event(target0, eventName, handleEvent);
+ on_event(target1, eventName, handleEvent);
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events interaction with drag and drop</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks that the pointercancel (and if needed lostpointercapture) is dispatched when drag starts.
+ <ol>
+ <li>Press down on the black square.</li>
+ <li>Move your pointer to purple square and release.</li>
+ <li>Repeat the first two steps.</li>
+ <li>Repeat the first two steps once again.</li>
+ <li>Repeat the first two steps once again.</li>
+ </ol>
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div draggable="true" id="target0"></div>
+ <div id="target1"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/idlharness.https.window.js b/testing/web-platform/tests/pointerevents/idlharness.https.window.js
new file mode 100644
index 0000000000..e6e84fa9c7
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/idlharness.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: timeout=long
+
+'use strict';
+
+// https://w3c.github.io/pointerevents/
+
+idl_test(
+ ['pointerevents'],
+ ['uievents', 'html', 'dom'],
+ idl_array => {
+ idl_array.add_objects({
+ Document: ['document'],
+ Element: ['document.body'],
+ Window: ['window'],
+ Navigator: ['navigator'],
+ PointerEvent: ['new PointerEvent("type")']
+ });
+ }
+);
diff --git a/testing/web-platform/tests/pointerevents/inheritance.html b/testing/web-platform/tests/pointerevents/inheritance.html
new file mode 100644
index 0000000000..0bf39bcb4d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/inheritance.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Inheritance of touch-action</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action does not inherit.">
+<meta name="assert" content="touch-action initial value is auto.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+assert_not_inherited('touch-action', 'auto', 'none');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/mouse-pointer-boundary-events-for-shadowdom.html b/testing/web-platform/tests/pointerevents/mouse-pointer-boundary-events-for-shadowdom.html
new file mode 100644
index 0000000000..ea200d6c96
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/mouse-pointer-boundary-events-for-shadowdom.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<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>
+<script type="text/javascript" src="pointerevent_support.js"></script>
+
+<h1>PointerEvent: Verifies that mouse boundary events don't point to shadow-dom</h1>
+
+<input id="target" style="margin: 20px">
+
+<script>
+function name(node) {
+ return node? node.tagName : "(null)";
+}
+
+promise_test(async () => {
+ var targetEvents = ["mouseout", "pointerout", "mouseover", "pointerover"];
+ var receivedEvents = [];
+ var moveReceived = false;
+
+
+ targetEvents.forEach(function(eventName) {
+ window.addEventListener(eventName, function(e) {
+ var eventDetails = e.type +
+ " target=" + name(e.target) +
+ " relatedTarget=" + name(e.relatedTarget);
+ receivedEvents.push(eventDetails);
+ });
+ });
+ window.addEventListener('pointermove', () => { moveReceived = true; });
+ var rect = document.getElementById("target").getBoundingClientRect();
+
+ await new test_driver.Actions()
+ .addPointer("default-mouse")
+ .pointerMove(Math.ceil(rect.left - 10), Math.ceil(rect.top - 10))
+ .send()
+ await resolveWhen(() => { return moveReceived == true });
+ receivedEvents = [];
+ moveReceived = false;
+
+ await new test_driver.Actions()
+ .addPointer("default-mouse")
+ .pointerMove(Math.ceil(rect.left + 10), Math.ceil(rect.top + 10))
+ .send()
+ await resolveWhen(() => { return moveReceived == true });
+
+ assert_array_equals(receivedEvents, [
+ "pointerout target=BODY relatedTarget=INPUT",
+ "pointerover target=INPUT relatedTarget=BODY",
+ "mouseout target=BODY relatedTarget=INPUT",
+ "mouseover target=INPUT relatedTarget=BODY",
+ ], "Moved into <input>");
+
+
+ receivedEvents = [];
+ moveReceived = false;
+
+ await new test_driver.Actions()
+ .addPointer("default-mouse")
+ .pointerMove(Math.ceil(rect.left - 10), Math.ceil(rect.top - 10))
+ .send()
+ await resolveWhen(() => { return moveReceived == true });
+
+ assert_array_equals(receivedEvents, [
+ "pointerout target=INPUT relatedTarget=BODY",
+ "pointerover target=BODY relatedTarget=INPUT",
+ "mouseout target=INPUT relatedTarget=BODY",
+ "mouseover target=BODY relatedTarget=INPUT",
+ ], "Moved out of <input>");
+}, "PointerEvent: Verifies that mouse boundary events don't point to shadow-dom");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/parsing/touch-action-computed.html b/testing/web-platform/tests/pointerevents/parsing/touch-action-computed.html
new file mode 100644
index 0000000000..59255bec45
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-computed.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Pointer Events: getComputedStyle().touchAction</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("touch-action", "auto");
+test_computed_value("touch-action", "none");
+test_computed_value("touch-action", "manipulation");
+
+test_computed_value("touch-action", "pan-x");
+test_computed_value("touch-action", "pan-y");
+test_computed_value("touch-action", "pan-x pan-y");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/parsing/touch-action-invalid.html b/testing/web-platform/tests/pointerevents/parsing/touch-action-invalid.html
new file mode 100644
index 0000000000..979dc252d3
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-invalid.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Pointer Events: parsing touch-action with invalid values</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action supports only the grammar 'auto | none | [ pan-x || pan-y ] | manipulation'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("touch-action", "auto none");
+test_invalid_value("touch-action", "manipulation pan-x");
+test_invalid_value("touch-action", "pan-y pan-x pan-y");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/parsing/touch-action-valid.html b/testing/web-platform/tests/pointerevents/parsing/touch-action-valid.html
new file mode 100644
index 0000000000..9bb5d023ed
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-valid.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Pointer Events: parsing touch-action with valid values</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action supports the full grammar 'auto | none | [ pan-x || pan-y ] | manipulation'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("touch-action", "auto");
+test_valid_value("touch-action", "none");
+test_valid_value("touch-action", "manipulation");
+
+// [ pan-x || pan-y ]
+test_valid_value("touch-action", "pan-x");
+test_valid_value("touch-action", "pan-y");
+test_valid_value("touch-action", "pan-y pan-x", "pan-x pan-y");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-in-iframe.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-in-iframe.html
new file mode 100644
index 0000000000..bf5c53566d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-in-iframe.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+
+<div style="pointer-events: none">
+ <iframe id="iframe"></iframe>
+</div>
+<div style="height: 200vh"></div>
+
+<script>
+ promise_test(async (t) => {
+ let iframe = document.getElementById("iframe");
+ await new Promise((resolve) => {
+ iframe.onload = resolve;
+ iframe.srcdoc = `
+<style>
+ #scroller {
+ overflow: auto;
+ height: 100px;
+ border: 2px solid blue;
+ }
+ .spacer {
+ height: 200vh;
+ }
+</style>
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+`;
+ });
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [
+ window,
+ iframe.contentWindow,
+ iframe.contentDocument.getElementById("scroller")
+ ];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 50, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-scrollbar.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-scrollbar.html
new file mode 100644
index 0000000000..f3dc4d6c7b
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-scrollbar.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+<style>
+ body {
+ margin: 0;
+ }
+
+ #scroller {
+ overflow: auto;
+ width: 300px;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ }
+
+ .spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("scroller")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ // The cursor is expected on the scrollbar, but if it's not (e.g. if the
+ // scrollbar is overlay and isn't show), the test should still pass.
+ const actions = new test_driver.Actions().scroll(295, 200, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-in-iframe.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-in-iframe.html
new file mode 100644
index 0000000000..e05bb915fb
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-in-iframe.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+
+<div style="pointer-events: none">
+ <iframe id="iframe"></iframe>
+</div>
+<div style="height: 200vh"></div>
+
+<script>
+ promise_test(async (t) => {
+ let iframe = document.getElementById("iframe");
+ await new Promise((resolve) => {
+ iframe.onload = resolve;
+ iframe.srcdoc = `
+<style>
+ #scroller {
+ overflow: auto;
+ height: 100px;
+ border: 2px solid blue;
+ will-change: scroll-position;
+ }
+ .spacer {
+ height: 200vh;
+ }
+</style>
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+`;
+ });
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [
+ window,
+ iframe.contentWindow,
+ iframe.contentDocument.getElementById("scroller")
+ ];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 50, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-scrollbar.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-scrollbar.html
new file mode 100644
index 0000000000..3acf1527b7
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change-scrollbar.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+<style>
+ body {
+ margin: 0;
+ }
+
+ #scroller {
+ overflow: auto;
+ width: 300px;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ will-change: scroll-position;
+ }
+
+ .spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("scroller")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ // The cursor is expected on the scrollbar, but if it's not (e.g. if the
+ // scrollbar is overlay and isn't show), the test should still pass.
+ const actions = new test_driver.Actions().scroll(295, 200, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change.html
new file mode 100644
index 0000000000..1fc3a2fdd0
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll-will-change.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+<style>
+ #scroller {
+ overflow: auto;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ will-change: scroll-position;
+ }
+
+ .spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("scroller")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 200, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll.html b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll.html
new file mode 100644
index 0000000000..e69eaaf61b
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointer-events-none-skip-scroll.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+<style>
+ #scroller {
+ overflow: auto;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ }
+
+ .spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<div id="scroller">
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("scroller")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 200, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html
new file mode 100644
index 0000000000..5d73702aca
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html
@@ -0,0 +1,223 @@
+<!DOCTYPE HTML>
+<title>Enter/leave events fired to parent after child is added</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+ const pointer_type = location.search.substring(1);
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ let logged_event_prefix = "";
+ let received_compat_mouse_events = false;
+
+ function logEvent(e) {
+ if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET) {
+ event_log.push(e.type + "@" + e.target.id);
+ }
+ if (e.type.startsWith("mouse")) {
+ received_compat_mouse_events = true;
+ }
+ }
+
+ function attachChild(e) {
+ if (e.eventPhase == e.AT_TARGET) {
+ parent.appendChild(child);
+ event_log.push("(child-attached)");
+ }
+ }
+
+ let child_moved = false;
+
+ function moveChild(e) {
+ if (!child_moved) {
+ child_moved = true;
+ parent.appendChild(child);
+ event_log.push("(child-moved)");
+ }
+ }
+
+ function setup() {
+ const logged_event_suffixes =
+ ["over", "out", "enter", "leave", "down", "up"];
+ let targets = document.getElementsByClassName("target");
+ for (let i = 0; i < targets.length; i++) {
+ logged_event_suffixes.forEach(suffix => {
+ targets[i].addEventListener("pointer" + suffix, logEvent);
+ targets[i].addEventListener("mouse" + suffix, logEvent);
+ });
+ }
+ }
+
+ function addPromiseTestForNewChild(attaching_event,
+ tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child attached at ${attaching_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+
+ // We started with child attached to ease event listener setup above.
+ parent.removeChild(child);
+
+ parent.addEventListener(attaching_event, attachChild);
+ test.add_cleanup(() => {
+ parent.removeEventListener(attaching_event, attachChild);
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ function addPromiseTestForMovedChild(mover_event,
+ tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child moved at ${mover_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+ child_moved = false;
+
+ child.addEventListener(mover_event, moveChild);
+ test.add_cleanup(() => {
+ child.removeEventListener(mover_event, moveChild);
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ setup();
+
+ // Tests for dispatched pointer events.
+ addPromiseTestForNewChild("pointerdown", "pointer", [
+ "pointerover@parent", "pointerenter@parent",
+ "pointerdown@parent", "(child-attached)",
+ "pointerout@parent", "pointerover@child", "pointerenter@child",
+ "pointerup@child",
+ "pointerdown@child", "pointerup@child",
+ "pointerout@child", "pointerleave@child", "pointerleave@parent"
+ ]);
+ addPromiseTestForNewChild("pointerup", "pointer", [
+ "pointerover@parent", "pointerenter@parent",
+ "pointerdown@parent", "pointerup@parent", "(child-attached)",
+ "pointerout@parent", "pointerover@child", "pointerenter@child",
+ "pointerdown@child", "pointerup@child",
+ "pointerout@child", "pointerleave@child", "pointerleave@parent"
+ ]);
+ addPromiseTestForMovedChild("pointerdown", "pointer", [
+ "pointerover@child", "pointerenter@parent", "pointerenter@child",
+ "pointerdown@child", "(child-moved)",
+ "pointerover@child", "pointerenter@child",
+ "pointerup@child",
+ "pointerdown@child", "pointerup@child",
+ "pointerout@child", "pointerleave@child", "pointerleave@parent"
+ ]);
+ addPromiseTestForMovedChild("pointerup", "pointer", [
+ "pointerover@child", "pointerenter@parent", "pointerenter@child",
+ "pointerdown@child", "pointerup@child", "(child-moved)",
+ "pointerover@child", "pointerenter@child",
+ "pointerdown@child", "pointerup@child",
+ "pointerout@child", "pointerleave@child", "pointerleave@parent"
+ ]);
+
+ // Same tests for dispatched compatibility mouse events.
+ addPromiseTestForNewChild("mousedown", "mouse", [
+ "mouseover@parent", "mouseenter@parent",
+ "mousedown@parent", "(child-attached)",
+ "mouseout@parent", "mouseover@child", "mouseenter@child",
+ "mouseup@child",
+ "mousedown@child", "mouseup@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForNewChild("mouseup", "mouse", [
+ "mouseover@parent", "mouseenter@parent",
+ "mousedown@parent", "mouseup@parent", "(child-attached)",
+ "mouseout@parent", "mouseover@child", "mouseenter@child",
+ "mousedown@child", "mouseup@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForMovedChild("mousedown", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "(child-moved)",
+ "mouseover@child", "mouseenter@child",
+ "mouseup@child",
+ "mousedown@child", "mouseup@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForMovedChild("mouseup", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "mouseup@child", "(child-moved)",
+ "mouseover@child", "mouseenter@child",
+ "mousedown@child", "mouseup@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html
new file mode 100644
index 0000000000..7c404d8c6c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html
@@ -0,0 +1,200 @@
+<!DOCTYPE HTML>
+<!--
+ Tentative due to:
+ https://github.com/w3c/pointerevents/issues/492
+-->
+<title>
+ Enter/leave events fired to parent after child is added
+ right before compat mouse-event
+</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+ const pointer_type = location.search.substring(1);
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ let logged_event_prefix = "";
+ let received_compat_mouse_events = false;
+
+ function logEvent(e) {
+ if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET) {
+ event_log.push(e.type + "@" + e.target.id);
+ }
+ if (e.type.startsWith("mouse")) {
+ received_compat_mouse_events = true;
+ }
+ }
+
+ function attachChild(e) {
+ if (e.eventPhase == e.AT_TARGET) {
+ parent.appendChild(child);
+ event_log.push("(child-attached)");
+ }
+ }
+
+ let child_moved = false;
+
+ function moveChild(e) {
+ if (!child_moved) {
+ child_moved = true;
+ parent.appendChild(child);
+ event_log.push("(child-moved)");
+ }
+ }
+
+ function setup() {
+ const logged_event_suffixes =
+ ["over", "out", "enter", "leave", "down", "up"];
+ let targets = document.getElementsByClassName("target");
+ for (let i = 0; i < targets.length; i++) {
+ logged_event_suffixes.forEach(suffix => {
+ targets[i].addEventListener("pointer" + suffix, logEvent);
+ targets[i].addEventListener("mouse" + suffix, logEvent);
+ });
+ targets[i].addEventListener("click", logEvent);
+ }
+ }
+
+ function addPromiseTestForNewChild(attaching_event,
+ tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child attached at ${attaching_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+
+ // We started with child attached to ease event listener setup above.
+ parent.removeChild(child);
+
+ parent.addEventListener(attaching_event, attachChild);
+ test.add_cleanup(() => {
+ parent.removeEventListener(attaching_event, attachChild);
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ function addPromiseTestForMovedChild(mover_event,
+ tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child moved at ${mover_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+ child_moved = false;
+
+ child.addEventListener(mover_event, moveChild);
+ test.add_cleanup(() => {
+ child.removeEventListener(mover_event, moveChild);
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ setup();
+
+ // Tests for dispatched compatibility mouse events
+ // after DOM modification through pointer events.
+ addPromiseTestForNewChild("pointerdown", "mouse", [
+ "mouseover@parent", "mouseenter@parent",
+ "mousedown@parent", "(child-attached)",
+ "mouseover@child", "mouseenter@child",
+ "mouseup@child", "click@parent",
+ "mousedown@child", "mouseup@child", "click@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForNewChild("pointerup", "mouse", [
+ "mouseover@parent", "mouseenter@parent",
+ "mousedown@parent", "mouseup@parent", "(child-attached)", "click@parent",
+ "mouseover@child", "mouseenter@child",
+ "mousedown@child", "mouseup@child", "click@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForMovedChild("pointerdown", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "(child-moved)",
+ "mouseover@child", "mouseenter@child",
+ "mouseup@child", "click@child",
+ "mousedown@child", "mouseup@child", "click@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+ addPromiseTestForMovedChild("pointerup", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "mouseup@child", "(child-moved)", "click@child",
+ "mouseover@child", "mouseenter@child",
+ "mousedown@child", "mouseup@child", "click@child",
+ "mouseout@child", "mouseleave@child", "mouseleave@parent"
+ ]);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html
new file mode 100644
index 0000000000..7b2a4eeb80
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html
@@ -0,0 +1,145 @@
+<!DOCTYPE HTML>
+<title>Enter/leave events fired to parent after child is removed</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+ const pointer_type = location.search.substring(1);
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ let logged_event_prefix = "";
+ let received_compat_mouse_events = false;
+
+ function logEvent(e) {
+ if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET) {
+ event_log.push(e.type + "@" + e.target.id);
+ }
+ if (e.type.startsWith("mouse")) {
+ received_compat_mouse_events = true;
+ }
+ }
+
+ function removeChild() {
+ parent.removeChild(child);
+ event_log.push("(child-removed)");
+ }
+
+ function setup() {
+ const logged_event_suffixes =
+ ["over", "out", "enter", "leave", "down", "up"];
+ let targets = document.getElementsByClassName("target");
+ for (let i = 0; i < targets.length; i++) {
+ logged_event_suffixes.forEach(suffix => {
+ targets[i].addEventListener("pointer" + suffix, logEvent);
+ targets[i].addEventListener("mouse" + suffix, logEvent);
+ });
+ }
+ }
+
+ function addPromiseTest(remover_event, tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child removal at ${remover_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+
+ child.addEventListener(remover_event, removeChild);
+ test.add_cleanup(() => {
+ child.removeEventListener(remover_event, removeChild);
+ if (!child.parentElement) {
+ parent.appendChild(child);
+ }
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ setup();
+
+ // Tests for dispatched pointer events.
+ addPromiseTest("pointerdown", "pointer",
+ pointer_type == "mouse"
+ // `pointerup` after removing the child should not cause `pointerover`
+ // on the parent if the pointer type is hoverable because pointer boundary
+ // events should be fired only when the hoverable pointer is actually
+ // moved.
+ ? [
+ "pointerover@child", "pointerenter@parent", "pointerenter@child",
+ "pointerdown@child", "(child-removed)", "pointerup@parent",
+ "pointerover@parent", "pointerdown@parent", "pointerup@parent",
+ "pointerout@parent", "pointerleave@parent"
+ ]
+ : [
+ "pointerover@child", "pointerenter@parent", "pointerenter@child",
+ "pointerdown@child", "(child-removed)", "pointerover@parent", "pointerup@parent",
+ "pointerdown@parent", "pointerup@parent",
+ "pointerout@parent", "pointerleave@parent"
+ ]);
+ addPromiseTest("pointerup", "pointer", [
+ "pointerover@child", "pointerenter@parent", "pointerenter@child",
+ "pointerdown@child", "pointerup@child", "(child-removed)",
+ "pointerover@parent", "pointerdown@parent", "pointerup@parent",
+ "pointerout@parent", "pointerleave@parent"
+ ]);
+
+ // Same tests for dispatched compatibility mouse events.
+ addPromiseTest("mousedown", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "(child-removed)", "mouseover@parent", "mouseup@parent",
+ "mousedown@parent", "mouseup@parent",
+ "mouseout@parent", "mouseleave@parent"
+ ]);
+ addPromiseTest("mouseup", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "mouseup@child", "(child-removed)",
+ "mouseover@parent", "mousedown@parent", "mouseup@parent",
+ "mouseout@parent", "mouseleave@parent"
+ ]);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html
new file mode 100644
index 0000000000..ec6ffce926
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<!--
+ Tentative due to:
+ https://github.com/w3c/pointerevents/issues/492
+-->
+<title>
+ Enter/leave events fired to parent after child is removed
+ right before compat mouse-event
+</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+ const pointer_type = location.search.substring(1);
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ let logged_event_prefix = "";
+ let received_compat_mouse_events = false;
+
+ function logEvent(e) {
+ if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET) {
+ event_log.push(e.type + "@" + e.target.id);
+ }
+ if (e.type.startsWith("mouse")) {
+ received_compat_mouse_events = true;
+ }
+ }
+
+ function removeChild() {
+ parent.removeChild(child);
+ event_log.push("(child-removed)");
+ }
+
+ function setup() {
+ const logged_event_suffixes =
+ ["over", "out", "enter", "leave", "down", "up"];
+ let targets = document.getElementsByClassName("target");
+ for (let i = 0; i < targets.length; i++) {
+ logged_event_suffixes.forEach(suffix => {
+ targets[i].addEventListener("pointer" + suffix, logEvent);
+ targets[i].addEventListener("mouse" + suffix, logEvent);
+ });
+ targets[i].addEventListener("click", logEvent);
+ }
+ }
+
+ function addPromiseTest(remover_event, tested_event_prefix, expected_events) {
+ const test_name = `${tested_event_prefix} events from ${pointer_type} `+
+ `received before/after child removal at ${remover_event}`;
+
+ promise_test(async test => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+
+ child.addEventListener(remover_event, removeChild);
+ test.add_cleanup(() => {
+ child.removeEventListener(remover_event, removeChild);
+ if (!child.parentElement) {
+ parent.appendChild(child);
+ }
+ });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(-30, -30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(30, 30, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ if (tested_event_prefix == "mouse" && !received_compat_mouse_events) {
+ expected_events = [];
+ }
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "events received");
+ }, test_name);
+ }
+
+ setup();
+
+ // Tests for dispatched compatibility mouse events
+ // after DOM modification through pointer events.
+ addPromiseTest("pointerdown", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "(child-removed)", "mouseover@parent", "mousedown@parent", "mouseup@parent", "click@parent",
+ "mousedown@parent", "mouseup@parent", "click@parent",
+ "mouseout@parent", "mouseleave@parent"
+ ]);
+ addPromiseTest("pointerup", "mouse", [
+ "mouseover@child", "mouseenter@parent", "mouseenter@child",
+ "mousedown@child", "(child-removed)", "mouseover@parent", "mouseup@parent", "click@parent",
+ "mousedown@parent", "mouseup@parent", "click@parent",
+ "mouseout@parent", "mouseleave@parent"
+ ]);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_attributes.html b/testing/web-platform/tests/pointerevents/pointerevent_attributes.html
new file mode 100644
index 0000000000..13fb30f61e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_attributes.html
@@ -0,0 +1,301 @@
+<!doctype html>
+<html>
+<head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <meta name="variant" content="?mouse">
+ <meta name="variant" content="?pen">
+ <meta name="variant" content="?mouse-right">
+ <meta name="variant" content="?pen-right">
+ <meta name="variant" content="?touch">
+ <meta name="variant" content="?mouse-nonstandard">
+ <meta name="variant" content="?pen-nonstandard">
+ <meta name="variant" content="?mouse-right-nonstandard">
+ <meta name="variant" content="?pen-right-nonstandard">
+ <meta name="variant" content="?touch-nonstandard">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <style>
+ html {
+ touch-action: none;
+ }
+
+ div {
+ padding: 0;
+ }
+
+ #square1 {
+ background-color: green;
+ border: 1px solid black;
+ height: 50px;
+ width: 50px;
+ margin-bottom: 3px;
+ display: inline-block;
+ }
+
+ #innerFrame {
+ position: relative;
+ margin-bottom: 3px;
+ margin-left: 0;
+ top: 0;
+ left: 0;
+ }
+ </style>
+</head>
+<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>
+<script>
+let frameLoaded = undefined;
+const frameLoadedPromise = new Promise(resolve => {
+ frameLoaded = resolve;
+});
+</script>
+<body>
+ <div id="square1"></div>
+ <div>
+ <iframe onLoad = "frameLoaded()" id="innerFrame" srcdoc='
+ <style>
+ html {
+ touch-action: none;
+ }
+ #square2 {
+ background-color: green;
+ border: 1px solid black;
+ height: 50px;
+ width: 50px;
+ display: inline-block;
+ }
+ </style>
+ <body>
+ <div id="square2"></div>
+ </body>
+ '></iframe>
+ </div>
+ <!-- Used to detect a sentinel event. Once triggered, all other events must
+ have been processed. -->
+ <div>
+ <button id="done">done</button>
+ </div>
+</body>
+<script>
+ window.onload = runTests();
+
+ async function runTests() {
+
+ const queryStringFragments = location.search.substring(1).split('-');
+ const pointerType = queryStringFragments[0];
+ const button = queryStringFragments[1] === "right" ? "right" : undefined;
+ const standard = !(queryStringFragments[queryStringFragments.length - 1] === "nonstandard");
+
+ const eventList = [
+ 'pointerover',
+ 'pointerenter',
+ 'pointerdown',
+ 'pointerup',
+ 'pointerout',
+ 'pointerleave',
+ 'pointermove'
+ ];
+
+ function injectScrubGesture(element) {
+ const doneButton = document.getElementById('done');
+ const actions = new test_driver.Actions();
+
+ let buttonArguments =
+ (button == 'right') ? { button: actions.ButtonType.RIGHT }
+ : undefined;
+
+ // The following comments refer to the first event of each type since
+ // that is what is being validated in the test.
+ return actions
+ .addPointer('pointer1', pointerType)
+ // The pointermove, pointerover and pointerenter events will be
+ // triggered here with a hover pointer.
+ .pointerMove(0, -20, { origin: element })
+ // Pointerdown triggers pointerover, pointerenter with a non-hover
+ // pointer type.
+ .pointerDown(buttonArguments)
+ // This move triggers pointermove with a non-hover pointer-type.
+ .pointerMove(0, 20, { origin: element })
+ // The pointerout and pointerleave events are triggered here with a
+ // touch pointer.
+ .pointerUp(buttonArguments)
+ // An addition move outside of the target bounds is required to trigger
+ // pointerout & pointerleave events with a hover pointer.
+ .pointerMove(0, 0)
+ .send();
+ }
+
+ // Processing a click or tap on the done button is used to signal that all
+ // other events should have beem handled. This is used to catch unhandled
+ // events that would otherwise result in a timeout.
+ function clickOrTapDone() {
+ const doneButton = document.getElementById('done');
+ const pointerupPromise = getEvent('pointerup', doneButton);
+ const actionPromise = new test_driver.Actions()
+ .addPointer('pointer1', 'touch')
+ .pointerMove(0, 0, {origin: doneButton})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ return actionPromise.then(pointerupPromise);
+ }
+
+ function verifyButtonAttributes(event) {
+ let downButton, upButton, downButtons, upButtons;
+ if (button == 'right') {
+ downButton = 2;
+ downButtons = 2;
+ upButton = 2;
+ upButtons = 0;
+ } else {
+ // defaults to left button click
+ downButton = 0;
+ downButtons = 1;
+ upButton = 0;
+ upButtons = 0;
+ }
+ const expectationsHover = {
+ // Pointer over, enter, and move are processed before the button press.
+ pointerover: { button: -1, buttons: 0 },
+ pointerenter: { button: -1, buttons: 0 },
+ pointermove: { button: -1, buttons: 0 },
+ // Button status changes on pointer down and up.
+ pointerdown: { button: downButton, buttons: downButtons },
+ pointerup: { button: upButton, buttons: upButtons },
+ // Pointer out and leave are processed after the button release.
+ pointerout: { button: -1, buttons: 0 },
+ pointerleave: { button: -1, buttons: 0 }
+ };
+ const expectationsNoHover = {
+ // We don't see pointer events except during a touch gesture.
+ // Move is the only pointer event where the "button" click state is not
+ // changing. All other pointer events are associated with the start or
+ // end of a touch gesture.
+ pointerover: { button: 0, buttons: 1 },
+ pointerenter: { button: 0, buttons: 1 },
+ pointerdown: { button: 0, buttons: 1 },
+ pointermove: { button: -1, buttons: 1 },
+ pointerup: { button: 0, buttons: 0 },
+ pointerout: { button: 0, buttons: 0 },
+ pointerleave: { button: 0, buttons: 0 }
+ };
+ const expectations =
+ (pointerType == 'touch') ? expectationsNoHover : expectationsHover;
+
+ assert_equals(event.button, expectations[event.type].button,
+ `Button attribute on ${event.type}`);
+ assert_equals(event.buttons, expectations[event.type].buttons,
+ `Buttons attribute on ${event.type}`);
+ }
+
+ function verifyPosition(event) {
+ const boundingRect = event.target.getBoundingClientRect();
+
+ // With a touch pointer type, the pointerout and pointerleave will trigger
+ // on pointerup while clientX and clientY are still within the target's
+ // bounds. With a hover pointer, these events will be triggered only after
+ // clientX or clientY are out of the target's bounds.
+ if (pointerType != 'touch' &&
+ (event.type == 'pointerout' || event.type == 'pointerleave')) {
+ assert_true(
+ boundingRect.left > event.clientX ||
+ boundingRect.right < event.clientX ||
+ boundingRect.top > event.clientY ||
+ boundingRect.bottom < event.clientY,
+ `clientX/clientY is outside the element bounds for ${event.type} event`);
+ } else {
+ assert_true(
+ boundingRect.left <= event.clientX &&
+ boundingRect.right >= event.clientX,
+ `clientX is within the expected range for ${event.type} event`);
+ assert_true(
+ boundingRect.top <= event.clientY &&
+ boundingRect.bottom >= event.clientY,
+ `clientY is within the expected range for ${event.type} event`);
+ }
+ }
+
+ function verifyEventAttributes(event, testNamePrefix) {
+ verifyButtonAttributes(event);
+ verifyPosition(event);
+ assert_true(event.isPrimary, 'isPrimary attribute is true');
+ check_PointerEvent(event, testNamePrefix, standard);
+ }
+
+ function pointerPromise(test, testNamePrefix, type, target) {
+ let rejectCallback = undefined;
+ promise = new Promise((resolve, reject) => {
+ // Store a reference to the promise rejection functions, which would
+ // otherwise not be visible outside the promise object. If the callback
+ // remains set when the deadline is reached, it means that the promise
+ // will not get resolved and should be rejected.
+ rejectCallback = reject;
+ const pointerEventListener = event => {
+ rejectCallback = undefined;
+ assert_equals(event.type, type, `type attribute for ${type} event`);
+ event.preventDefault();
+ resolve(event);
+ };
+ target.addEventListener(type, pointerEventListener, { once: true });
+ test.add_cleanup(() => {
+ // Just in case of an assert prior to the events being triggered.
+ document.removeEventListener(type, pointerEventListener,
+ { once: true });
+ });
+ }).then(result => { verifyEventAttributes(result, testNamePrefix); },
+ error => { assert_unreached(error); });
+ promise.deadlineReached = () => {
+ // If the event has not been received, the promise will not be
+ // fulfilled, leading to a timeout. Reject the promise if still pending.
+ if (rejectCallback) {
+ rejectCallback(`missing ${type} event`);
+ }
+ }
+ return promise;
+ }
+
+ async function runPointerEventsTest(test, testNamePrefix, target) {
+ assert_true(['mouse', 'pen', 'touch'].indexOf(pointerType) >= 0,
+ `Unexpected pointer type (${pointerType})`);
+
+ const promises = [];
+ eventList.forEach(type => {
+ // Create a promise for each event type. If clicking on the done button
+ // is detected before an event's promise is resolved, then the promise
+ // will be rejected. Otherwise, the attributes for the event are
+ // verified.
+ promises.push(pointerPromise(test, testNamePrefix, type, target));
+ });
+
+ await injectScrubGesture(target);
+
+ // The injected gestures consist of a shrub on a button followed by a
+ // click on the done button. The promise is only resolved after the
+ // done click is detected. At this stage all other events must have been
+ // processed. Any unresolved promises in the list will be rejected to
+ // avoid a test timeout. The rejection will trigger a test failure.
+ await clickOrTapDone().then(promises.map(p => p.deadlineReached()));
+
+ // Once all promises are resolved, all event attributes have been
+ // successfully verified.
+ return Promise.all(promises);
+ }
+
+ promise_test(t => {
+ const square1 = document.getElementById('square1');
+ return runPointerEventsTest(t, '', square1);
+ }, 'Test pointer events in the main document');
+
+ promise_test(async t => {
+ const innerFrame = document.getElementById('innerFrame');
+ await frameLoadedPromise;
+ const square2 = innerFrame.contentDocument.getElementById('square2');
+ return runPointerEventsTest(t, 'Inner Frame', square2);
+ }, 'Test pointer events in an iframe');
+ }
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_auxclick_is_a_pointerevent.html b/testing/web-platform/tests/pointerevents/pointerevent_auxclick_is_a_pointerevent.html
new file mode 100644
index 0000000000..b32bee9e8f
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_auxclick_is_a_pointerevent.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<title>auxclick is a PointerEvent</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<!-- TODO: Does any platform support auxclick with touch? -->
+<link rel="help" href="https://github.com/w3c/pointerevents/pull/317">
+<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="pointerevent_support.js"></script>
+
+<input id="target" style="margin: 20px">
+
+<script>
+ 'use strict';
+ const target = document.getElementById("target");
+ const pointer_type = location.search.substring(1);
+ const test_pointer = pointer_type + "TestPointer";
+
+ function assertAuxclickProperties(auxclick_event, pointerdown_event) {
+ assert_equals(auxclick_event.constructor, window.PointerEvent,
+ "auxclick should use a PointerEvent constructor");
+ assert_true(auxclick_event instanceof PointerEvent,
+ "auxclick should be a PointerEvent");
+ assert_not_equals(auxclick_event.pointerId, -1,
+ "auxclick.pointerId should not be -1");
+ assert_equals(auxclick_event.pointerType, pointer_type,
+ "auxclick.pointerType should match test action pointerType");
+ assert_equals(auxclick_event.composed, true,
+ "auxclick.composed should be true");
+
+ if (pointerdown_event) {
+ assert_equals(auxclick_event.pointerId, pointerdown_event.pointerId,
+ "auxclick.pointerId should match pointerdown.pointerId");
+ }
+ }
+
+ promise_test(async test => {
+ let actions = new test_driver.Actions();
+ actions = actions
+ .addPointer(test_pointer, pointer_type)
+ .pointerMove(0,0, {origin:target, sourceName:test_pointer})
+ .pointerDown({button:actions.ButtonType.MIDDLE, sourceName:test_pointer})
+ .pointerUp({button:actions.ButtonType.MIDDLE, sourceName:test_pointer});
+ let pointerdown_prevented = preventDefaultPointerdownOnce(target, test);
+ let pointerdown_promise = getEvent("pointerdown", target, test);
+ let auxclick_promise = getEvent("auxclick", target, test);
+
+ await actions.send();
+ await pointerdown_prevented;
+ let pointerdown_event = await pointerdown_promise;
+ let auxclick_event = await auxclick_promise;
+
+ assertAuxclickProperties(auxclick_event, pointerdown_event);
+ }, "auxclick using " + pointer_type + " is a PointerEvent with correct properties");
+
+ promise_test(async test => {
+ let actions = new test_driver.Actions();
+ actions = actions
+ .addPointer(test_pointer, pointer_type)
+ .pointerMove(0,0, {origin:target, sourceName:test_pointer})
+ .pointerDown({button:actions.ButtonType.MIDDLE, sourceName:test_pointer})
+ .pointerUp({button:actions.ButtonType.MIDDLE, sourceName:test_pointer});
+ let auxclick_promise = getEvent("auxclick", target, test);
+
+ await actions.send();
+ let auxclick_event = await auxclick_promise;
+
+ assertAuxclickProperties(auxclick_event);
+ }, "auxclick using " + pointer_type + " is a PointerEvent with correct properties"
+ + " when no other PointerEvent listeners are present");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers.html b/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers.html
new file mode 100644
index 0000000000..066f3a7ea8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Boundary event sequence at implicit capture release</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event."/>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var event_log = [];
+ var start_logging = false;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ event_log = [];
+ start_logging = false;
+ }
+
+ function run() {
+ var test_pointer_event = setup_pointerevent_test("Event sequence at implicit release on click", ["mouse"]);
+ var actions_promise;
+
+ var target = document.getElementById("target");
+ var capture_target = document.getElementById("capture-target");
+
+ All_Pointer_Events.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type == "pointerdown") {
+ capture_target.setPointerCapture(event.pointerId);
+ start_logging = true;
+ } else if (start_logging) {
+ event_log.push(event.type + '@' + event.target.id);
+ }
+ });
+
+ on_event(capture_target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ event_log.push(event.type + '@' + event.target.id);
+ if (event.type == 'lostpointercapture') {
+ step_timeout(function() {
+ test_pointer_event.step(function () {
+ var expected_events = "pointerup, lostpointercapture, pointerout, pointerleave";
+ assert_equals(event_log.join(", "), "pointerout@target, pointerleave@target, pointerover@capture-target, pointerenter@capture-target, gotpointercapture@capture-target, pointerup@capture-target, lostpointercapture@capture-target, pointerout@capture-target, pointerleave@capture-target, pointerover@target, pointerenter@target");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointer_event.done();
+ });
+ }, 200);
+ }
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <style>
+ #target {
+ margin: 20px;
+ background-color: black;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Boundary event sequence at implicit capture release</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event.
+ </h4>
+ <ol>
+ <li>Click on the black box with mouse and do not move the mouse after or during the click.</li>
+ </ol>
+ <div id="capture-target"></div>
+ <div id="target"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_in_capturing.html b/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_in_capturing.html
new file mode 100644
index 0000000000..b3a3fe7fe1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_boundary_events_in_capturing.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events boundary events in capturing tests</title>
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events boundary events in capturing</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4 id="desc">
+ This automated test checks the boundary events of pointer events while the capturing
+ changes. This test cannot be run manually.
+ </h4>
+ <div id="target0" class="touchActionNone"></div>
+ <div id="capturer">Do not hover over or touch this element.</div>
+ <div id="log"></div>
+ </body>
+ <script>
+ let input_source = location.search.substring(1);
+
+ let events_received = [];
+ let log_events = false;
+
+ // The events are handled as follows:
+ // - When "target0" receives a "pointerdown", the pointer is captured to "capturer" and event
+ // logging starts.
+ // - During event logging, all boundary events and "got/lostpointercapture" events to both
+ // "target0" and "capturer" are logged.
+ // - The first "pointermove" at "capturer" releases the pointer capture, so that subsequent
+ // "pointermove"/"pointerup" events go to "target0".
+ // - Event logging ends when "target0" receives a "pointerup".
+
+ function logEvent(event, element) {
+ if (log_events)
+ events_received.push(event.type + "@" + element.id);
+ }
+
+ let target0 = document.getElementById("target0");
+ let capturer = document.getElementById("capturer");
+
+ target0.addEventListener("pointerdown", event => {
+ capturer.setPointerCapture(event.pointerId);
+ });
+
+ capturer.addEventListener("pointermove", event => {
+ if (capturer.hasPointerCapture(event.pointerId))
+ capturer.releasePointerCapture(event.pointerId);
+ });
+
+ let other_event_types = [ "pointercancel",
+ "pointerover", "pointerout", "pointerenter", "pointerleave",
+ "gotpointercapture", "lostpointercapture" ];
+ other_event_types.forEach(event_name => {
+ [target0, capturer].forEach(target => {
+ target.addEventListener(event_name, event => logEvent(event, target));
+ });
+ });
+
+ promise_test(async () => {
+ // Start logging events after the pointerdown in the drag.
+ getEvent("pointerdown", target0).then(() => {
+ log_events = true;
+ });
+
+ let pointerup_promise = getEvent("pointerup", target0);
+ pointerup_promise.then(() => {
+ log_events = false;
+ });
+
+ await pointerDragInTarget(input_source, target0, "right");
+
+ // Wait for all events in the drag to have been dispatched.
+ await pointerup_promise;
+
+ const expected_events = [
+ "pointerout@target0", "pointerleave@target0",
+ "pointerover@capturer", "pointerenter@capturer",
+ "gotpointercapture@capturer",
+ "lostpointercapture@capturer",
+ "pointerout@capturer", "pointerleave@capturer",
+ "pointerover@target0", "pointerenter@target0"
+ ];
+ assert_array_equals(events_received, expected_events);
+
+ updateDescriptionComplete();
+ }, "Boundary events around pointer capture and release");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_bubble_ancestors_none.html b/testing/web-platform/tests/pointerevents/pointerevent_bubble_ancestors_none.html
new file mode 100644
index 0000000000..8908079cc8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_bubble_ancestors_none.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+<head>
+<title>PointerEvent: Events still bubble to ancestors with pointer-events: none </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-vendor.js"></script>
+<style>
+#wrapper:active {
+ pointer-events: none;
+}
+</style>
+</head>
+<body>
+<div id="parent">
+ <div id="wrapper">
+ <button>click me</button>
+ </div>
+</div>
+
+<script>
+promise_test(function() {
+ const parentClickedPromise = new Promise(r => {
+ document.getElementById("parent").addEventListener("click", r);
+ });
+
+ const click = test_driver.click(document.querySelector("button"));
+
+ return Promise.all([click, parentClickedPromise]);
+})
+</script>
+</body>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_bubble_display_none.html b/testing/web-platform/tests/pointerevents/pointerevent_bubble_display_none.html
new file mode 100644
index 0000000000..d4c6ba1db7
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_bubble_display_none.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+<head>
+<title>PointerEvent: Events still bubble to ancestors with display: none </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-vendor.js"></script>
+<style>
+#wrapper:active {
+ display: none;
+}
+
+#parent {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+</style>
+</head>
+<body>
+<div id="parent">
+ <div id="wrapper">
+ <button>click me</button>
+ </div>
+</div>
+
+<script>
+promise_test(function() {
+ const parentClickedPromise = new Promise(r => {
+ document.getElementById("parent").addEventListener("click", r);
+ });
+
+ const click = test_driver.click(document.querySelector("button"));
+
+ return Promise.all([click, parentClickedPromise]);
+})
+</script>
+</body>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_bubble_mousedown_mouseup_different_target.html b/testing/web-platform/tests/pointerevents/pointerevent_bubble_mousedown_mouseup_different_target.html
new file mode 100644
index 0000000000..c504e0df43
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_bubble_mousedown_mouseup_different_target.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+<head>
+<title>PointerEvent: Events still bubble to ancestors with mousedown causes mouseup to be a different target</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-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<style>
+#wrapper {
+ display: none;
+}
+
+#parent {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+
+button {
+ width: 200px;
+ height: 200px;
+}
+</style>
+</head>
+<body>
+<div id="parent">
+ <div id="wrapper">
+ <button>click me</button>
+ </div>
+</div>
+
+<script>
+promise_test(function() {
+ const parentClickedPromise = new Promise(r => {
+ document.getElementById("parent").addEventListener("click", r);
+ });
+
+ document.getElementById("parent").addEventListener("mousedown", function() {
+ document.getElementById("wrapper").style.display = "block";
+ });
+
+ const click = test_driver.click(document.getElementById("parent"));
+
+ return Promise.all([click, parentClickedPromise]);
+})
+</script>
+</body>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_capture_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_capture_mouse.html
new file mode 100644
index 0000000000..61a933afd8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_capture_mouse.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Set/Release capture</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body>
+ <h1>Pointer Events capture test</h1>
+ <h4>
+ Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
+ <ol>
+ <li> Move your mouse over the black rectangle. pointermove event should be logged in the black rectangle</li>
+ <li> Move your mouse over the purple rectangle. pointerover event should be logged in the purple rectangle</li>
+ <li> Press and hold left mouse button over "Set Capture" button. "gotpointercapture" should be logged in the black rectangle</li>
+ <li> Move your mouse anywhere. pointermove should be logged in the black rectangle</li>
+ <li> Move your mouse over the purple rectangle. Nothig should happen</li>
+ <li> Move your mouse over the black rectangle. pointermove should be logged in the black rectangle</li>
+ <li> Release left mouse button. "lostpointercapture" should be logged in the black rectangle</li>
+ </ol>
+ </h4>
+ Test passes if the proper behaviour of the events is observed.
+ <div id="target0"></div>
+ <br>
+ <div id="target1"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var isPointerCapture = false;
+ var pointermoveNoCaptureGot0 = false;
+ var pointermoveCaptureGot0 = false;
+ var pointermoveNoCaptureGot1 = false;
+ var ownEventForTheCapturedTargetGot = false;
+ var count=0;
+ var event_log = [];
+ var actions_promise1;
+ var actions_promise2;
+ var actions_promise3;
+ var actions_promise4;
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var target1 = document.getElementById('target1');
+ var captureButton = document.getElementById('btnCapture');
+
+ var test_gotpointercapture = async_test("gotpointercapture event received");
+ var test_lostpointercapture = async_test("lostpointercapture event received");
+
+ window.onload = function() {
+ on_event(captureButton, 'pointerdown', function(e) {
+ if(isPointerCapture == false) {
+ isPointerCapture = true;
+ sPointerCapture(e);
+ }
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ actions_promise3.then( () => {
+ test_gotpointercapture.done();
+ });
+ event_log.push('gotpointercapture@target0');
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ actions_promise4.then( () => {
+ test_lostpointercapture.done();
+ });
+ isPointerCapture = false;
+ event_log.push('lostpointercapture@target0');
+ });
+
+ run();
+ }
+
+ function run() {
+ var test_pointermove0 = async_test("pointerover event for black rectangle received")
+ var test_pointermove1 = async_test("pointerover event for purple rectangle received")
+
+ on_event(target0, "pointermove", function (event) {
+ detected_pointertypes[ event.pointerType ] = true;
+ if(!pointermoveNoCaptureGot0) {
+ actions_promise2 = actions_promise1.then( () => {
+ test_pointermove0.done();
+ event_log.push('pointermove@target0');
+ pointermoveNoCaptureGot0 = true;
+ // Second dispatch a pointer move to target1.
+ return new test_driver.Actions().pointerMove(0, 0, {origin: target1}).send();
+ });
+ }
+ if(isPointerCapture) {
+ if(!pointermoveCaptureGot0) {
+ test(function() {
+ assert_equals(event.relatedTarget, null, "relatedTarget is null when the capture is set")
+ }, "relatedTarget is null when the capture is set. relatedTarget is " + event.relatedTarget);
+ test(function() {
+ assert_true((event.clientX < target0.getBoundingClientRect().left)||
+ (event.clientX > target0.getBoundingClientRect().right)||
+ (event.clientY < target0.getBoundingClientRect().top)||
+ (event.clientY > target0.getBoundingClientRect().bottom),
+ "pointermove received for captured element while out of it")
+ }, "pointermove received for captured element while out of it");
+ event_log.push('pointermove@target0');
+ pointermoveCaptureGot0 = true;
+ // Fourth dispatch a pointer move to target0.
+ actions_promise4 = actions_promise3.then( () => {
+ return new test_driver.Actions().pointerMove(0, 0, {origin: target0}).send();
+ });
+ }
+ if((event.clientX > target0.getBoundingClientRect().left)&&
+ (event.clientX < target0.getBoundingClientRect().right)&&
+ (event.clientY > target0.getBoundingClientRect().top)&&
+ (event.clientY < target0.getBoundingClientRect().bottom)&&
+ !ownEventForTheCapturedTargetGot) {
+ test(function() {
+ assert_true(true, "pointermove received for captured element while inside of it");
+ }, "pointermove received for captured element while inside of it");
+ event_log.push('pointermove@target0');
+ ownEventForTheCapturedTargetGot = true;
+ }
+ }
+ });
+
+ // First dispatch a pointer move to target0.
+ actions_promise1 = new test_driver.Actions().pointerMove(0, 0, {origin: target0}).send();
+
+ on_event(target1, "pointermove", function (event) {
+ test(function() {
+ assert_false(isPointerCapture, "pointermove shouldn't trigger for this target when capture is enabled");
+ }, "pointermove shouldn't trigger for the purple rectangle while the black rectangle has capture");
+
+ if(!pointermoveNoCaptureGot1) {
+ actions_promise3 = actions_promise2.then( () => {
+ test_pointermove1.done();
+ event_log.push('pointermove@target1');
+ pointermoveNoCaptureGot1 = true;
+ // Third, drag the mouse from btnCapture, target1 to target0.
+ return new test_driver.Actions()
+ .pointerMove(0, 0, {origin: btnCapture})
+ .pointerDown()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerMove(0, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ });
+ }
+ });
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_capture_suppressing_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_capture_suppressing_mouse.html
new file mode 100644
index 0000000000..0d7756335d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_capture_suppressing_mouse.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Set/Release capture</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body>
+ <div class="spacer"></div>
+ <div id="target0"></div>
+ <div class="spacer"></div>
+ <div id="target1"></div>
+ <div class="spacer"></div>
+ <input type="button" id="captureButton" value="Set Capture">
+ </body>
+ <script type='text/javascript'>
+ window.onload = () => {
+ let eventLog = [];
+ let nextUncheckedEventIndex = 0;
+ const target0 = document.getElementById('target0');
+ const target1 = document.getElementById('target1');
+ const captureButton = document.getElementById('captureButton');
+
+ function eventLabel(target, eventName) {
+ return `${eventName}@${target.id}`;
+ }
+
+ function recordEvent(target, eventName) {
+ eventLog.push(eventLabel(target, eventName));
+ }
+
+ // 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 = eventLabel(target, expectedEventNames[i]);
+ 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(", ")}`);
+ }
+
+ // Adds listeners for all element-pointerevent combinations. Each listener
+ // records the event for validation. the pointerdown event on the button
+ // triggers an extra step of triggering pointer capture.
+ function addEventListeners(t) {
+ // Adds a single event that is removed at the conclusion of the test.
+ const addListener = (target, eventName, fn) => {
+ const callback = (e) => {
+ recordEvent(target, eventName);
+ // Additional event handling is optional.
+ if (fn)
+ fn(e);
+ };
+ target.addEventListener(eventName, callback);
+ t.add_cleanup(() => {
+ target.removeEventListener(eventName, callback);
+ });
+ };
+ [target0, target1, captureButton].forEach(el => {
+ ['gotpointercapture', 'lostpointercapture', 'pointerenter',
+ 'pointerleave', 'pointermove', 'pointerout',
+ 'pointerover'].forEach(eventName => {
+ addListener(el, eventName);
+ });
+ });
+ addListener(captureButton, 'pointerdown', (e) => {
+ target0.setPointerCapture(e.pointerId);
+ });
+ t.add_cleanup(() => {
+ eventLog = [];
+ nextUncheckedEventIndex = 0;
+ });
+ }
+
+ // Trigger and wait for a pointer move. The wait is to ensure there is
+ // no coalescence of pointer move events.
+ async function moveTo(x, y, target) {
+ const movePromise = getEvent('pointermove', target);
+ let actions = new test_driver.Actions()
+ .pointerMove(x, y, { origin: target })
+ .send();
+ await actions;
+ await movePromise;
+ }
+
+ promise_test(async t => {
+ // Reset pointer position.
+ await moveTo(0, 0, document.body);
+ addEventListeners(t);
+ // Move to the first target.
+ await moveTo(0, 0, target0);
+ assert_next_events(target0,["pointerover", "pointerenter", "pointermove"],
+ 'Move to first target');
+ assert_empty_event_queue('Check after first move');
+
+ // Move to the second taret.
+ await moveTo(0, 0, target1);
+ assert_next_events(target0, ['pointerout', 'pointerleave'],
+ 'Exit first target');
+ assert_next_events(target1,
+ ["pointerover", "pointerenter", "pointermove"],
+ 'Move to second target');
+ assert_empty_event_queue('Check after second move');
+
+ // Move to the capture button.
+ await moveTo(0, 0, captureButton);
+ assert_next_events(target1, ['pointerout', 'pointerleave'],
+ 'Exit second target');
+ assert_next_events(
+ captureButton, ["pointerover", "pointerenter", "pointermove"],
+ 'Move to button');
+ assert_empty_event_queue('Check after third move');
+ }, 'Validate pointer events track pointer movement without pointer '
+ + 'capture.');
+
+ async function runCaptureAndHoverTargetActionSequence(t, hoverTarget) {
+ const pointerUpPromise = getEvent('pointerup', document);
+ const actionsPromise =
+ new test_driver.Actions()
+ // Start outside capture button.
+ .pointerMove(0, 0)
+ .pointerDown()
+ .pointerUp()
+ // Move to the capture button
+ .pointerMove(0, 0, {origin: captureButton})
+ // Trigger pointer capture
+ .pointerDown()
+ // Hover over the target element
+ .pointerMove(10, 0, {origin: hoverTarget})
+ // Release capture
+ .pointerUp()
+ .send();
+ await actionsPromise;
+ await pointerUpPromise;
+ }
+
+ promise_test(async t => {
+ // Reset pointer position.
+ await moveTo(0, 0, document.body);
+ addEventListeners(t);
+
+ // On entry for this sub-test, the pointer is at the document origin.
+ await runCaptureAndHoverTargetActionSequence(t, captureButton);
+ assert_next_events(
+ captureButton,
+ ['pointerover', 'pointerenter', 'pointermove'],
+ 'Move to button from origin');
+ assert_next_events(
+ captureButton,
+ ['pointerdown', 'pointerout', 'pointerleave'],
+ 'Pointer down on button to trigger capture');
+ assert_next_events(
+ target0,
+ ['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
+ 'Capture triggered while hovering over button');
+ assert_next_events(
+ target0,
+ ['lostpointercapture', 'pointerout', 'pointerleave'],
+ 'Lose pointer capture while hovering over button');
+ // Post pointer capture.
+ assert_next_events(
+ captureButton,
+ ['pointerover', 'pointerenter'],
+ 'Post capture while hovering over button');
+ assert_empty_event_queue('Check after button hover');
+
+ // On entry for this sub-test, the pointer is over the button.
+ await runCaptureAndHoverTargetActionSequence(t, target0);
+ assert_next_events(
+ captureButton,
+ ['pointerout', 'pointerleave'],
+ 'Move from button to document origin');
+ assert_next_events(
+ captureButton,
+ ['pointerover', 'pointerenter', 'pointermove'],
+ 'Move from document origin to button');
+ assert_next_events(
+ captureButton,
+ [ 'pointerdown', 'pointerout', 'pointerleave'],
+ 'Pointer down on button to trigger capture');
+ assert_next_events(
+ target0,
+ ['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
+ 'Capture triggered while hovering over capture target');
+ assert_next_events(
+ target0,
+ ['lostpointercapture'],
+ 'Lose pointer capture while hovering over capture target');
+ assert_empty_event_queue('Check after hover on capture target');
+
+
+ // On entry for this sub-test, the pointer is over the capture target.
+ await runCaptureAndHoverTargetActionSequence(t, target1);
+ assert_next_events(
+ target0,
+ ['pointerout', 'pointerleave'],
+ 'Move from capture target to button');
+ assert_next_events(
+ captureButton,
+ ['pointerover', 'pointerenter', 'pointermove'],
+ 'Move from capture target to button');
+ assert_next_events(
+ captureButton,
+ [ 'pointerdown', 'pointerout', 'pointerleave'],
+ 'Pointer down on button to trigger capture');
+ assert_next_events(
+ target0,
+ ['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
+ 'Capture triggered while hovering over non-capture target');
+ assert_next_events(
+ target0,
+ ['lostpointercapture', 'pointerout', 'pointerleave'],
+ 'Lose pointer capture while hovering over non-capture target');
+ assert_next_events(
+ target1,
+ ["pointerover", "pointerenter"],
+ 'Post capture while hovering over non-capture target');
+ assert_empty_event_queue('Check after hover on non-capture target ');
+ }, 'Test pointer capture.');
+ };
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html
new file mode 100644
index 0000000000..40dbbed72f
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html
@@ -0,0 +1,147 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Change touch-action on pointerdown</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ background: black;
+ width: 700px;
+ height: 430px;
+ color: white;
+ overflow-y: auto;
+ overflow-x: auto;
+ white-space: nowrap;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4>Test Description: Press and hold your touch. Try to scroll text in any direction.
+ Then release your touch and try to scroll again. Expected: no panning.
+ </h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0" style="touch-action: auto;">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var styleIsChanged = false;
+ var scrollIsReceived = false;
+ var scrollReceivedCorrectly = false;
+ var firstTouchCompleted = false;
+ var countToPass = 50;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ setup({ explicit_done: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, 'scroll', function(event) {
+ if(!scrollIsReceived && firstTouchCompleted) {
+ test(function() {
+ failOnScroll();
+ }, "scroll was received while shouldn't");
+ scrollIsReceived = true;
+ }
+ scrollReceivedCorrectly = true;
+ });
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ if(!styleIsChanged) {
+ var before = document.getElementById('target0').style.touchAction;
+
+ document.getElementById('target0').style.touchAction = 'none';
+
+ var after = document.getElementById('target0').style.touchAction;
+
+ test(function() {
+ assert_not_equals(before, after, "touch-action was changed");
+ }, "touch-action was changed");
+
+ styleIsChanged = true;
+ }
+ });
+
+ on_event(target0, 'pointerup', function(event) {
+ firstTouchCompleted = true;
+ });
+
+ // Inject touch inputs and wait for all the actions finish to end the test.
+ touchScrollInTarget(target0, 'down').then(function() {
+ test(function () {
+ assert_true(scrollReceivedCorrectly, "scroll should be received before the test finishes");
+ }, "scroll should be received before the test finishes");
+ done();
+ });
+ }
+ </script>
+ <h1>touch-action: auto to none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_click_during_capture.html b/testing/web-platform/tests/pointerevents/pointerevent_click_during_capture.html
new file mode 100644
index 0000000000..e7448c7a8b
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_click_during_capture.html
@@ -0,0 +1,138 @@
+<!doctype html>
+<html>
+ <head>
+ <title>click event target during capture</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <style>
+ .box {
+ margin: 10px;
+ }
+ #grey {
+ background: grey;
+ }
+ #blue {
+ background: blue;
+ }
+ #green {
+ background: green;
+ }
+ </style>
+ <script type="text/javascript">
+ PhaseEnum = {
+ Phase1: "phase1",
+ Phase2: "phase2",
+ Phase1WithCapturing: "phase1withcapturing",
+ Phase2WithCapturing: "phase2withcapturing",
+ }
+ var phase;
+ var receivedEvents;
+
+ function resetTestState() {
+ phase = PhaseEnum.Phase1;
+ receivedEvents = [];
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("click target during capture", ['mouse']);
+ var grey = document.getElementById('grey');
+ var blue = document.getElementById('blue');
+ var green = document.getElementById('green');
+ var actions_promise;
+
+ ['gotpointercapture', 'lostpointercapture', 'pointerdown', 'pointerup', 'click'].forEach(function(eventName) {
+ [grey, blue, green].forEach(function(target) {
+ target.addEventListener(eventName, function(event) {
+ if (event.eventPhase == event.AT_TARGET) {
+ receivedEvents.push(event.type + '@' + target.id);
+ if (phase == PhaseEnum.Phase1 && target == green && event.type == 'click') {
+ test(function() {
+ assert_equals(receivedEvents.join(','), 'pointerdown@green,pointerup@green,click@green', 'An element should only receive click when it is the first common ancestor of pointerdown and pointerup targets');
+ }, "Click target when pointerup/down targeted at the same element with no capture");
+ phase = PhaseEnum.Phase2;
+ receivedEvents = [];
+ }
+ if (phase == PhaseEnum.Phase2 && target == grey && event.type == 'click') {
+ test(function() {
+ assert_equals(receivedEvents.join(','), 'pointerdown@blue,pointerup@green,click@grey', 'An element should only receive click when it is the first common ancestor of pointerdown and pointerup targets');
+ }, "Click target when pointerup/down targeted at different elements with no capture");
+ phase = PhaseEnum.Phase1WithCapturing;
+ receivedEvents = [];
+ }
+ if (target == blue && event.type == 'lostpointercapture') {
+ if (phase == PhaseEnum.Phase1WithCapturing) {
+ test_pointerEvent.step(function() {
+ assert_equals(receivedEvents.join(','), 'pointerdown@green,gotpointercapture@blue,pointerup@blue,click@blue,lostpointercapture@blue', 'An element should only receive click when it is the first common ancestor of pointerdown and pointerup targets');
+ });
+ phase = PhaseEnum.Phase2WithCapturing;
+ receivedEvents = [];
+ } else if (phase == PhaseEnum.Phase2WithCapturing) {
+ test_pointerEvent.step(function() {
+ assert_equals(receivedEvents.join(','), 'pointerdown@blue,gotpointercapture@blue,pointerup@blue,click@blue,lostpointercapture@blue', 'An element should only receive click when it is the first common ancestor of pointerdown and pointerup targets');
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+ }
+ if (event.type == 'pointerdown' && (target == blue || target == green)) {
+ if (phase == PhaseEnum.Phase1WithCapturing || phase == PhaseEnum.Phase2WithCapturing)
+ blue.setPointerCapture(event.pointerId);
+ }
+ }
+ });
+ });
+ });
+
+ // Inject mouse inputs.
+ //
+ // TODO(mustaq@chromium.org): It is no longer testable manually.
+ // Switch it to a promise_test to simplify the code, and remove
+ // the instructions on HTML.
+ var actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: green})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: blue})
+ .pointerDown()
+ .pointerMove(0, 0, {origin: green})
+ .pointerUp()
+ .pointerMove(0, 0, {origin: green})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: blue})
+ .pointerDown()
+ .pointerMove(0, 0, {origin: green})
+ .pointerUp()
+ .send();
+
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: click event during capture</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>Test Description:
+ Click event should be sent to the first common ancestor of the pointerdown and pointerup targets.
+ <ol>
+ <li>Click on the green box with the left button of mouse.</li>
+ <li>Press down the left button on the blue box and drag to the green box and release the button.</li>
+ <li>Repeat the two steps above once again.</li>
+ </ol>
+ </h4>
+ <br>
+ <div>
+ <div id="grey" class="box">
+ <div id="green" class="box"></div>
+ <div id="blue" class="box"></div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent.html b/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent.html
new file mode 100644
index 0000000000..ceaae68db2
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<title>click is a PointerEvent</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<meta name="variant" content="?touch">
+<link rel="help" href="https://github.com/w3c/pointerevents/pull/317">
+<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="pointerevent_support.js"></script>
+
+<input id="target" style="margin: 20px">
+
+<iframe src="resources/minimal.html" height="20" width="20"></iframe>
+
+<script>
+'use strict';
+const pointer_type = location.search.substring(1);
+let subframe_loaded = getMessageData("subframe-loaded", frames[0]);
+
+function assertClickProperties(
+ click_event, window_object, pointerdown_event, pointerup_event) {
+ assert_equals(click_event.constructor, window_object.PointerEvent,
+ "click should use a PointerEvent constructor");
+ assert_true(click_event instanceof window_object.PointerEvent,
+ "click should be a PointerEvent instance");
+ assert_equals(click_event.composed, true, "click.composed should be true");
+
+ if (pointerdown_event) {
+ assert_equals(click_event.pointerId, pointerdown_event.pointerId,
+ "click.pointerId should match pointerdown.pointerId");
+ assert_equals(click_event.pointerType, pointerdown_event.pointerType,
+ "click.pointerType should match pointerdown.pointerType");
+ }
+
+ if (pointerup_event) {
+ assert_equals(click_event.pointerId, pointerup_event.pointerId,
+ "click.pointerId should match pointerup.pointerId");
+ assert_equals(click_event.pointerType, pointerup_event.pointerType,
+ "click.pointerType should match pointerup.pointerType");
+ }
+}
+
+promise_test(async test => {
+ const target = document.getElementById("target");
+
+ let pointerdown_prevented = preventDefaultPointerdownOnce(target, test);
+
+ let pointerdown_promise = getEvent("pointerdown", target, test);
+ let pointerup_promise = getEvent("pointerup", target, test);
+ let click_promise = getEvent("click", target, test);
+
+ await clickInTarget(pointer_type, target);
+
+ await pointerdown_prevented;
+ let pointerdown_event = await pointerdown_promise;
+ let pointerup_event = await pointerup_promise;
+ let click_event = await click_promise;
+
+ assertClickProperties(click_event, this, pointerdown_event, pointerup_event);
+}, "click using " + pointer_type + " is a PointerEvent with correct properties");
+
+promise_test(async test => {
+ const target = document.getElementById("target");
+
+ let click_promise = getEvent("click", target, test);
+ await clickInTarget(pointer_type, target);
+
+ let click_event = await click_promise;
+
+ assertClickProperties(click_event, this);
+ assert_not_equals(click_event.pointerId, -1,
+ "click.pointerId should not be -1");
+}, "click using " + pointer_type + " is a PointerEvent with correct properties"
+ + " when no other PointerEvent listeners are present");
+
+promise_test(async test => {
+ await subframe_loaded;
+
+ const target = frames[0];
+ let pointerdown_promise = getEvent("pointerdown", target, test);
+ let pointerup_promise = getEvent("pointerup", target, test);
+ let click_promise = getEvent("click", target, test);
+
+ await clickInTarget(pointer_type, frames[0].document.body);
+
+ let pointerdown_event = await pointerdown_promise;
+ let pointerup_event = await pointerup_promise;
+ let click_event = await click_promise;
+
+ assertClickProperties(click_event, frames[0], pointerdown_event, pointerup_event);
+}, "click using " + pointer_type + " is a PointerEvent with correct properties"
+ + " in a subframe");
+
+
+// Run this part of the test only once, since it doesn't rely on the pointer_type.
+if (pointer_type == "mouse") {
+ promise_test(async test => {
+ const target = document.getElementById("target");
+ let click_promise = getEvent("click", target, test);
+ target.click();
+ let click_event = await click_promise;
+ assert_equals(click_event.pointerId, -1,
+ "click.pointerId should be -1");
+
+ target.type = "button";
+ target.focus();
+ click_promise = getEvent("click", target, test);
+ // Press enter
+ let actions = new test_driver.Actions()
+ .keyDown("\uE007")
+ .keyUp("\uE007");
+ await actions.send();
+ click_event = await click_promise;
+ assertClickProperties(click_event, this);
+ assert_equals(click_event.pointerId, -1,
+ "click.pointerId should be -1");
+ }, "click using " + pointer_type + " is a PointerEvent with correct properties"
+ + " using non-pointing device");
+}
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html b/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html
new file mode 100644
index 0000000000..fe18302ae4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<title>click is a PointerEvent</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<meta name="variant" content="?touch">
+<link rel="help" href="https://github.com/w3c/pointerevents/pull/317">
+<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>
+
+<input id="target" style="margin: 20px">
+
+<script>
+'use strict';
+let target = document.getElementById("target");
+let pointerId_one = 0;
+let pointerType_one = "";
+let pointerId_two = 0;
+let pointerType_two = "";
+let pointerId_three = 0;
+let pointerType_three = "";
+let inputSource = location.search.substring(1);
+const phase_one = 1;
+const phase_two = 2;
+const phase_three = 3;
+let pointerdown_phase = phase_one;
+let click_phase = phase_one;
+
+target.addEventListener("pointerdown", (e)=>{
+ if(pointerdown_phase === phase_one){
+ pointerdown_phase = phase_two;
+ pointerId_one = e.pointerId;
+ pointerType_one = e.pointerType;
+ }else if(pointerdown_phase === phase_two){
+ pointerdown_phase = phase_three;
+ pointerId_two = e.pointerId;
+ pointerType_two = e.pointerType;
+ }else if(pointerdown_phase === phase_three){
+ pointerId_three = e.pointerId;
+ pointerType_three = e.pointerType;
+ }
+});
+
+function test_pointer(e, pointerId, pointerType, click_phase){
+ assert_equals(e.constructor, window.PointerEvent, "click should use a PointerEvent constructor in phase " + click_phase);
+ assert_true(e instanceof PointerEvent, "click should be a PointerEvent in phase " + click_phase);
+ assert_equals(e.pointerId, pointerId, "click's pointerId should match the pointerId of the pointer event that triggers it in phase " + click_phase);
+ assert_equals(e.pointerType, pointerType, "click's pointerType should match the pointerType of the pointer event that triggers it in phase " + click_phase);
+}
+
+function testFunction(test){
+ return test.step_func(e=>{
+ if(click_phase === phase_one){
+ test_pointer(e, pointerId_one, pointerType_one, click_phase);
+ click_phase = phase_two;
+ }else if(click_phase === phase_two){
+ test_pointer(e, pointerId_two, pointerType_two, click_phase);
+ click_phase = phase_three;
+ }else if(click_phase === phase_three)
+ test_pointer(e, pointerId_three, pointerType_three, click_phase);
+ });
+}
+
+function run_test(pointerType){
+ promise_test((test) => new Promise((resolve, reject) => {
+ const testPointer = pointerType + "TestPointer";
+ let clickFunc = testFunction(test);
+ test.add_cleanup(() => {
+ target.removeEventListener("click", clickFunc);
+ pointerId_one = 0;
+ pointerType_one = "";
+ pointerId_two = 0;
+ pointerType_two = "";
+ pointerId_three = 0;
+ pointerType_three = "";
+ pointerdown_phase = phase_one;
+ click_phase = phase_one;
+ });
+ target.addEventListener("click", clickFunc);
+ let eventWatcher = new EventWatcher(test, target, ["click"]);
+ let actions = new test_driver.Actions();
+ actions = actions
+ .addPointer(testPointer, pointerType)
+ .pointerMove(0,0, {origin:target, sourceName:testPointer})
+ .pointerDown({sourceName:testPointer})
+ .pointerUp({sourceName:testPointer})
+ .pointerDown({sourceName:testPointer})
+ .pointerUp({sourceName:testPointer})
+ .pointerDown({sourceName:testPointer})
+ .pointerUp({sourceName:testPointer});
+ Promise.all([eventWatcher.wait_for(["click", "click", "click"]), actions.send()]).then(()=>resolve());
+ }), "click using " + pointerType + " is a PointerEvent");
+}
+
+run_test(inputSource);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_constructor.html b/testing/web-platform/tests/pointerevents/pointerevent_constructor.html
new file mode 100644
index 0000000000..af7345fafc
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_constructor.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerEvent: Constructor test</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ </head>
+ <body>
+ <h1>PointerEvent: Dispatch custom event</h1>
+ <h4>Test Description: This test checks if PointerEvent constructor works properly.</h4>
+ <div id="target0"></div>
+ <script>
+ async_test(function() {
+ var target0 = document.getElementById("target0");
+ // set values for non-default constructor
+ on_event(target0, "pointermove", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["getCoalescedEvents in event", "getCoalescedEvents" in event, false],
+ ["getPredictedEvents().length", event.getPredictedEvents().length, 2],
+ ["event.target", event.target, target0],
+ ["event.currentTarget", event.currentTarget, target0],
+ ["event.eventPhase", event.eventPhase, Event.AT_TARGET],
+ ["event.clientX", event.clientX, 310],
+ ["event.pointerType", event.pointerType, "pen"],
+ ["getPredictedEvents()[0].clientX", event.getPredictedEvents()[0].clientX, 320],
+ ["getPredictedEvents()[1].clientX", event.getPredictedEvents()[1].clientX, 330],
+ ]);
+ for (var i=0; i<event.getPredictedEvents().length; i++) {
+ var predictedEvent = event.getPredictedEvents()[i];
+ generate_tests(assert_equals, [
+ ["getPredictedEvents()[" + i + "].pointerId", predictedEvent.pointerId, event.pointerId],
+ ["getPredictedEvents()[" + i + "].pointerType", predictedEvent.pointerType, event.pointerType],
+ ["getPredictedEvents()[" + i + "].isPrimary", predictedEvent.isPrimary, event.isPrimary],
+ ["getPredictedEvents()[" + i + "].getPredictedEvents().length", predictedEvent.getPredictedEvents().length, 0],
+ ["getPredictedEvents()[" + i + "].target", predictedEvent.target, null],
+ ["getPredictedEvents()[" + i + "].currentTarget", predictedEvent.currentTarget, null],
+ ["getPredictedEvents()[" + i + "].eventPhase", predictedEvent.eventPhase, Event.NONE],
+ ["getPredictedEvents()[" + i + "].cancelable", predictedEvent.cancelable, false],
+ ["getPredictedEvents()[" + i + "].bubbles", predictedEvent.bubbles, false],
+ ["getPredictedEvents()[" + i + "].offsetX", predictedEvent.offsetX, 310 + (i==0?10:20)],
+ ["getPredictedEvents()[" + i + "].offsetY", predictedEvent.offsetY, 0],
+ ]);
+ }
+ }));
+ on_event(target0, "pointerout", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["default event.pointerType", event.pointerType, ""],
+ ["default getPredictedEvents().length", event.getPredictedEvents().length, 0],
+ ]);
+ }));
+ on_event(target0, "pointerup", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["type event.pointerType", event.pointerType, "foo"],
+ ]);
+ }));
+
+ on_event(window, "load", this.step_func_done(function() {
+ assert_not_equals(window.PointerEvent, undefined);
+
+ var pointerEventInitDict =
+ {
+ pointerId: 42,
+ pointerType: "pen",
+ isPrimary: true,
+ clientX: 300,
+ };
+
+ var p1 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p2 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p3 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p4 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.coalescedEvents = [p1, p2];
+ pointerEventInitDict.predictedEvents = [p3, p4];
+ pointerEventInitDict.clientX = 310;
+ var event = new PointerEvent("pointermove", pointerEventInitDict);
+ target0.dispatchEvent(event);
+
+ var pointerEventDefault = new PointerEvent("pointerout");
+ target0.dispatchEvent(pointerEventDefault);
+
+ var pointerEventType = new PointerEvent("pointerup", { pointerType: "foo" } );
+ target0.dispatchEvent(pointerEventType);
+ }, "PointerEvent constructor"));
+ })
+ </script>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_constructor.https.html b/testing/web-platform/tests/pointerevents/pointerevent_constructor.https.html
new file mode 100644
index 0000000000..3b278746e1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_constructor.https.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerEvent: Constructor test</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ </head>
+ <body>
+ <h1>PointerEvent: Dispatch custom event</h1>
+ <h4>Test Description: This test checks if PointerEvent constructor works properly.</h4>
+ <div id="target0"></div>
+ <script>
+ async_test(function() {
+ var target0 = document.getElementById("target0");
+ // set values for non-default constructor
+ on_event(target0, "pointermove", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["getCoalescedEvents().length", event.getCoalescedEvents().length, 2],
+ ["getPredictedEvents().length", event.getPredictedEvents().length, 2],
+ ["event.target", event.target, target0],
+ ["event.currentTarget", event.currentTarget, target0],
+ ["event.eventPhase", event.eventPhase, Event.AT_TARGET],
+ ["event.clientX", event.clientX, 310],
+ ["event.pointerType", event.pointerType, "pen"],
+ ["getCoalescedEvents()[0].clientX", event.getCoalescedEvents()[0].clientX, 300],
+ ["getCoalescedEvents()[1].clientX", event.getCoalescedEvents()[1].clientX, 310],
+ ["getPredictedEvents()[0].clientX", event.getPredictedEvents()[0].clientX, 320],
+ ["getPredictedEvents()[1].clientX", event.getPredictedEvents()[1].clientX, 330],
+ ]);
+ for (var i=0; i<event.getCoalescedEvents().length; i++) {
+ var coalescedEvent = event.getCoalescedEvents()[i];
+ generate_tests(assert_equals, [
+ ["getCoalescedEvents()[" + i + "].pointerId", coalescedEvent.pointerId, event.pointerId],
+ ["getCoalescedEvents()[" + i + "].pointerType", coalescedEvent.pointerType, event.pointerType],
+ ["getCoalescedEvents()[" + i + "].isPrimary", coalescedEvent.isPrimary, event.isPrimary],
+ ["getCoalescedEvents()[" + i + "].getCoalescedEvents().length", coalescedEvent.getCoalescedEvents().length, 0],
+ ["getCoalescedEvents()[" + i + "].getPredictedEvents().length", coalescedEvent.getPredictedEvents().length, 0],
+ ["getCoalescedEvents()[" + i + "].target", coalescedEvent.target, null],
+ ["getCoalescedEvents()[" + i + "].currentTarget", coalescedEvent.currentTarget, null],
+ ["getCoalescedEvents()[" + i + "].eventPhase", coalescedEvent.eventPhase, Event.NONE],
+ ["getCoalescedEvents()[" + i + "].cancelable", coalescedEvent.cancelable, false],
+ ["getCoalescedEvents()[" + i + "].bubbles", coalescedEvent.bubbles, false],
+ ["getCoalescedEvents()[" + i + "].offsetX", coalescedEvent.offsetX, 310 + (i==0?-10:0)],
+ ["getCoalescedEvents()[" + i + "].offsetY", coalescedEvent.offsetY, 0],
+ ]);
+ }
+ for (var i=0; i<event.getPredictedEvents().length; i++) {
+ var predictedEvent = event.getPredictedEvents()[i];
+ generate_tests(assert_equals, [
+ ["getPredictedEvents()[" + i + "].pointerId", predictedEvent.pointerId, event.pointerId],
+ ["getPredictedEvents()[" + i + "].pointerType", predictedEvent.pointerType, event.pointerType],
+ ["getPredictedEvents()[" + i + "].isPrimary", predictedEvent.isPrimary, event.isPrimary],
+ ["getPredictedEvents()[" + i + "].getCoalescedEvents().length", predictedEvent.getCoalescedEvents().length, 0],
+ ["getPredictedEvents()[" + i + "].getPredictedEvents().length", predictedEvent.getPredictedEvents().length, 0],
+ ["getPredictedEvents()[" + i + "].target", predictedEvent.target, null],
+ ["getPredictedEvents()[" + i + "].currentTarget", predictedEvent.currentTarget, null],
+ ["getPredictedEvents()[" + i + "].eventPhase", predictedEvent.eventPhase, Event.NONE],
+ ["getPredictedEvents()[" + i + "].cancelable", predictedEvent.cancelable, false],
+ ["getPredictedEvents()[" + i + "].bubbles", predictedEvent.bubbles, false],
+ ["getPredictedEvents()[" + i + "].offsetX", predictedEvent.offsetX, 310 + (i==0?10:20)],
+ ["getPredictedEvents()[" + i + "].offsetY", predictedEvent.offsetY, 0],
+ ]);
+ }
+ }));
+ on_event(target0, "pointerout", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["default event.pointerType", event.pointerType, ""],
+ ["default getCoalescedEvents().length", event.getCoalescedEvents().length, 0],
+ ["default getPredictedEvents().length", event.getPredictedEvents().length, 0],
+ ]);
+ }));
+ on_event(target0, "pointerup", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["type event.pointerType", event.pointerType, "foo"],
+ ]);
+ }));
+
+ on_event(window, "load", this.step_func_done(function() {
+ assert_not_equals(window.PointerEvent, undefined);
+
+ var pointerEventInitDict =
+ {
+ pointerId: 42,
+ pointerType: "pen",
+ isPrimary: true,
+ clientX: 300,
+ };
+
+ var p1 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p2 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p3 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.clientX += 10;
+ var p4 = new PointerEvent("pointermove", pointerEventInitDict);
+ pointerEventInitDict.coalescedEvents = [p1, p2];
+ pointerEventInitDict.predictedEvents = [p3, p4];
+ pointerEventInitDict.clientX = 310;
+ var event = new PointerEvent("pointermove", pointerEventInitDict);
+ target0.dispatchEvent(event);
+
+ var pointerEventDefault = new PointerEvent("pointerout");
+ target0.dispatchEvent(pointerEventDefault);
+
+ var pointerEventType = new PointerEvent("pointerup", { pointerType: "foo" } );
+ target0.dispatchEvent(pointerEventType);
+ }, "PointerEvent constructor"));
+ })
+ </script>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html b/testing/web-platform/tests/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html
new file mode 100644
index 0000000000..88ec1903ab
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<title>contexmenu is a PointerEvent</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<!-- TODO: Can we test "pen" just like "touch"? -->
+<link rel="help" href="https://github.com/w3c/pointerevents/pull/317">
+<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="pointerevent_support.js"></script>
+
+<input id="target" style="margin: 20px">
+
+<script>
+ 'use strict';
+ const target = document.getElementById("target");
+ const pointer_type = location.search.substring(1);
+
+ function assertContextmenuProperties(contextmenu_event, pointerdown_event) {
+ assert_equals(contextmenu_event.constructor, window.PointerEvent,
+ "contextmenu should use a PointerEvent constructor");
+ assert_true(contextmenu_event instanceof PointerEvent,
+ "contextmenu should be a PointerEvent");
+ assert_not_equals(contextmenu_event.pointerId, -1,
+ "contextmenu.pointerId should not be -1");
+ assert_equals(contextmenu_event.pointerType, pointer_type,
+ "contextmenu.pointerType should match test action pointerType");
+ assert_equals(contextmenu_event.composed, true,
+ "contextmenu.composed should be true");
+
+ if (pointerdown_event) {
+ assert_equals(contextmenu_event.pointerId, pointerdown_event.pointerId,
+ "contextmenu.pointerId should match pointerdown.pointerId");
+ }
+ }
+
+ promise_test(async test => {
+ const test_pointer = pointer_type + "TestPointer";
+
+ let actions = new test_driver.Actions();
+ actions = actions.addPointer(test_pointer, pointer_type)
+ .pointerMove(0,0, {sourceName:test_pointer, origin:target})
+ .pointerDown({sourceName:test_pointer, button:actions.ButtonType.RIGHT})
+ .pause(pointer_type === "touch" ? 1500 : 0, "pointer", {sourceName:test_pointer})
+ .pointerUp({sourceName:test_pointer, button:actions.ButtonType.RIGHT});
+ let pointerdown_prevented = preventDefaultPointerdownOnce(target, test);
+ let pointerdown_promise = getEvent("pointerdown", target, test);
+ let contextmenu_promise = getEvent("contextmenu", target, test);
+
+ await actions.send();
+ await pointerdown_prevented;
+ let pointerdown_event = await pointerdown_promise;
+ let contextmenu_event = await contextmenu_promise;
+
+ assertContextmenuProperties(contextmenu_event, pointerdown_event);
+ }, "contextmenu using " + pointer_type + " is a PointerEvent with correct properties");
+
+ promise_test(async test => {
+ const test_pointer = pointer_type + "TestPointer";
+
+ let actions = new test_driver.Actions();
+ actions = actions.addPointer(test_pointer, pointer_type)
+ .pointerMove(0,0, {sourceName:test_pointer, origin:target})
+ .pointerDown({sourceName:test_pointer, button:actions.ButtonType.RIGHT})
+ .pause(pointer_type === "touch" ? 1500 : 0, "pointer", {sourceName:test_pointer})
+ .pointerUp({sourceName:test_pointer, button:actions.ButtonType.RIGHT});
+ let contextmenu_promise = getEvent("contextmenu", target, test);
+
+ await actions.send();
+ let contextmenu_event = await contextmenu_promise;
+
+ assertContextmenuProperties(contextmenu_event);
+ }, "contextmenu using " + pointer_type + " is a PointerEvent with correct properties"
+ + " when no other PointerEvent listeners are present");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_disabled_form_control.html b/testing/web-platform/tests/pointerevents/pointerevent_disabled_form_control.html
new file mode 100644
index 0000000000..d253111115
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_disabled_form_control.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <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>
+ <script>
+ var inputSource = location.search.substring(1);
+ var detected_pointertypes = {};
+ var detected_eventTypes = {};
+ var eventList = ['pointerout', 'pointerover', 'pointerenter', 'pointermove', 'pointerdown', 'gotpointercapture', 'pointerup', 'lostpointercapture', 'pointerleave'];
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ }
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", [inputSource]);
+ var target = document.getElementById("target0");
+ var actions_promise;
+
+ eventList.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_eventTypes[event.type] = true;
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type === "pointerdown") {
+ target.setPointerCapture(event.pointerId);
+ }
+
+ if (Object.keys(detected_eventTypes).length == eventList.length) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+ });
+ });
+ on_event(target, "click", function (event) {
+ assert_unreached("click should not fire on disabled element");
+ });
+
+ // Inject inputs.
+ switch (inputSource) {
+ case "touch":
+ actions_promise = pointerDragInTarget(inputSource, target, 'right');
+ break;
+ case "mouse":
+ case "pen":
+ actions_promise = clickInTarget(inputSource, target).then(function() {
+ return new test_driver.Actions()
+ .addPointer(inputSource + "Pointer1", inputSource)
+ .pointerMove(0, 0, {origin: target})
+ .pointerMove(0, 0)
+ .send();
+ });
+ break;
+ }
+ }
+ </script>
+ <style>
+ #target0 {
+ background: purple;
+ border: 1px solid orange;
+ touch-action: none;
+ width: 200px;
+ height: 100px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1> Pointer Event: pointerevents fire to disabled form controls</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description:
+ Pointerevents are dispatched to disabled form controls.
+ </h4>
+ <div id="instructions">
+ Use the mouse or pen to press on the purple button. Or with touch, tap on the purple box.
+ </div>
+ <button id="target0" disabled>Disabled button</button>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture.html b/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture.html
new file mode 100644
index 0000000000..3db6acf8e5
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Element.hasPointerCapture test</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_pointerEvent = async_test("hasPointerCapture");
+ var listening_events = [
+ "pointerover",
+ "pointerenter",
+ "pointerout",
+ "pointerleave",
+ "pointermove",
+ "gotpointercapture"
+ ];
+ var set_capture_to_target0 = false;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+ var actions_promise;
+
+ on_event(target0, "pointerdown", function (e) {
+ detected_pointertypes[e.pointerType] = true;
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false,
+ "before target0.setPointerCapture, target0.hasPointerCapture should be false");
+ });
+ target1.setPointerCapture(e.pointerId);
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false,
+ "after target1.setPointerCapture, target0.hasPointerCapture should be false");
+ assert_equals(target1.hasPointerCapture(e.pointerId), true,
+ "after target1.setPointerCapture, target1.hasPointerCapture should be true");
+ });
+ target0.setPointerCapture(e.pointerId);
+ set_capture_to_target0 = true;
+ // hasPointerCapture will return true immediately after a call to setPointerCapture
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true,
+ "after target0.setPointerCapture, target0.hasPointerCapture should be true");
+ });
+ // hasPointerCapture will return false immediately after a call to releasePointerCapture
+ target0.releasePointerCapture(e.pointerId);
+ set_capture_to_target0 = false;
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false,
+ "after target0.releasePointerCapture, target0.hasPointerCapture should be false");
+ assert_equals(target1.hasPointerCapture(e.pointerId), false,
+ "after target0.releasePointerCapture, target1.hasPointerCapture should be false");
+ });
+ target0.setPointerCapture(e.pointerId);
+ set_capture_to_target0 = true;
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true,
+ "after target0.setPointerCapture, target0.hasPointerCapture should be true");
+ });
+ // If the element.hasPointerCapture is false element.releasePointerCapture does nothing
+ target1.releasePointerCapture(e.pointerId);
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true,
+ "while target1.hasPointerCapture is false, target1.releasePointerCapture should not change hasPointerCapture");
+ });
+ });
+
+ for (var i = 0; i < listening_events.length; i++) {
+ on_event(target0, listening_events[i], function (e) {
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), set_capture_to_target0,
+ "Received " + e.type + " target0.hasPointerCapture should be " + set_capture_to_target0);
+ });
+ });
+ }
+
+ on_event(target0, "pointerup", function (e) {
+ // Immediately after firing the pointerup or pointercancel events, a user agent must clear
+ // the pending pointer capture target override for the pointerId, and then run
+ // "Process Pending Pointer Capture" steps to fire lostpointercapture if necessary.
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true,
+ "pointerup target0.hasPointerCapture should be true");
+ });
+ set_capture_to_target0 = false;
+ });
+
+ on_event(target0, "lostpointercapture", function (e) {
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false,
+ "pointerup target0.hasPointerCapture should be false");
+ });
+ });
+
+ on_event(target1, "pointerup", function (e) {
+ test_pointerEvent.step(function () {
+ assert_equals(target1.hasPointerCapture(e.pointerId), false,
+ "pointerup target1.hasPointerCapture should be false");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(0, 10, {origin: target0})
+ .pointerMove(0, 20, {origin: target0})
+ .pointerMove(0, -10, {origin: target1})
+ .pointerMove(0, 0, {origin: target1})
+ .pointerMove(0, 10, {origin: target1})
+ .pointerUp()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Element.hasPointerCapture test</h1>
+ <h4>
+ Test Description: This test checks if Element.hasPointerCapture returns value correctly
+ <ol>
+ <li> Press black rectangle and do not release
+ <li> Move your pointer to purple rectangle
+ <li> Release the pointer
+ <li> Click purple rectangle
+ </ol>
+ </h4>
+ <p>
+ <div id="target0" touch-action:none></div>
+ <div id="target1" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture_release_pending_capture.html b/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture_release_pending_capture.html
new file mode 100644
index 0000000000..e75dff8675
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture_release_pending_capture.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Element.hasPointerCapture test after the pending pointer capture element releases pointer capture</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var actions_promise;
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_pointerEvent = async_test("hasPointerCapture test after the pending pointer capture element releases pointer capture");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (e) {
+ detected_pointertypes[e.pointerType] = true;
+ target0.setPointerCapture(e.pointerId);
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true, "After target0.setPointerCapture, target0.hasPointerCapture should return true");
+ });
+ });
+
+ on_event(target0, "gotpointercapture", function (e) {
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), true, "After target0 received gotpointercapture, target0.hasPointerCapture should return true");
+ });
+ target1.setPointerCapture(e.pointerId);
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false, "After target1.setPointerCapture, target0.hasPointerCapture should return false");
+ assert_equals(target1.hasPointerCapture(e.pointerId), true, "After target1.setPointerCapture, target1.hasPointerCapture should return true");
+ });
+ target1.releasePointerCapture(e.pointerId);
+ test_pointerEvent.step(function () {
+ assert_equals(target0.hasPointerCapture(e.pointerId), false, "After target1.releasePointerCapture, target0.hasPointerCapture should be false");
+ assert_equals(target1.hasPointerCapture(e.pointerId), false, "After target1.releasePointerCapture, target1.hasPointerCapture should be false");
+ });
+ });
+
+ on_event(target1, "gotpointercapture", function (e) {
+ test_pointerEvent.step(function () {
+ assert_true(false, "target1 should never receive gotpointercapture in this test");
+ });
+ });
+
+ on_event(target0, "lostpointercapture", function (e) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(10, 10, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 10, {origin: target1})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Element.hasPointerCapture test after the pending pointer capture element releases pointer capture</h1>
+ <h4>
+ Test Description: This test checks if Element.hasPointerCapture returns value correctly after the pending pointer capture element releases pointer capture
+ <ol>
+ <li> Press black rectangle and do not release
+ <li> Move your pointer to purple rectangle
+ <li> Release the pointer
+ </ol>
+ </h4>
+ <p>
+ <div id="target0" touch-action:none></div>
+ <div id="target1" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_fractional_coordinates.html b/testing/web-platform/tests/pointerevents/pointerevent_fractional_coordinates.html
new file mode 100644
index 0000000000..7d18b9f2b7
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_fractional_coordinates.html
@@ -0,0 +1,156 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events coordinates with fractional values</title>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <style>
+ #innerFrame {
+ transform: scale(5);
+ width: 60px;
+ height: 60px;
+ margin-left: 120px;
+ margin-top: 120px;
+ border: 0.01px solid black;
+ }
+ </style>
+ <script>
+ "use strict";
+ const input = location.search.substring(1);
+ const eventList = [
+ "pointerdown",
+ "pointerup",
+ "pointermove",
+ "pointerover",
+ "pointerout",
+ "pointerenter",
+ "pointerleave",
+ "click"
+ ];
+ let eventsWithFractions = {};
+ let clickedTargetList = [];
+
+ function resetTestState() {
+ eventsWithFractions = {};
+ clickedTargetList = [];
+ }
+
+ function checkPointerEventCoordinates(event) {
+ if (event.clientX != Math.floor(event.clientX) ||
+ event.clientY != Math.floor(event.clientY))
+ eventsWithFractions[event.type] = true;
+ }
+
+ function testInputType(inputSource) {
+ const scale = 5;
+ const width = 3;
+ const height = 3;
+ const targetFrame = document.querySelector('#innerFrame');
+ const frameRect = targetFrame.getBoundingClientRect();
+ const frameLeft = frameRect.left;
+ const frameTop = frameRect.top;
+
+ const targets = [{x: 10, y: 10}, {x: 30, y: 50}, {x: 50, y: 30}]
+ const xPositions = []
+ const yPositions = []
+ for (let i = 0; i < targets.length; i++) {
+ xPositions.push((targets[i].x + width / 2.0) * scale + frameLeft);
+ yPositions.push((targets[i].y + height / 2.0) * scale + frameTop);
+ }
+ return sendInputAt(inputSource, xPositions[0], yPositions[0]).then(function() {
+ return sendInputAt(inputSource, xPositions[1], yPositions[1]);
+ }).then(function() {
+ return sendInputAt(inputSource, xPositions[2], yPositions[2]);
+ });
+ }
+
+ function sendInputAt(inputSource, xPosition, yPosition) {
+ if (inputSource == "touch") {
+ return new test_driver.Actions()
+ .addPointer("touchPointer1", "touch")
+ .pointerMove(Math.ceil(xPosition), Math.ceil(yPosition))
+ .pointerDown()
+ .pointerMove(Math.ceil(xPosition + 1), Math.ceil(yPosition + 1))
+ .pointerUp()
+ .send();
+ } else {
+ return new test_driver.Actions()
+ .pointerMove(Math.ceil(xPosition), Math.ceil(yPosition))
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ }
+
+ function run() {
+ const test_pointerEvent = setup_pointerevent_test("pointerevent events in capturing", [input]);
+ const innerFrame = document.getElementById('innerFrame');
+ const innerDocument = innerFrame.contentDocument;
+ ['s1', 's2', 's3'].forEach(function(id){
+ const target = innerDocument.getElementById(id);
+ eventList.forEach(function(eventName) {
+ on_event(target, eventName, checkPointerEventCoordinates);
+ });
+
+ on_event(target, "click", function (event) {
+ if (!(event.target.id in clickedTargetList)) {
+ clickedTargetList.push(event.target.id);
+ }
+ if (clickedTargetList.length == 3) {
+ test(function () {
+ eventList.forEach(function(eventName){
+ if (eventName == "click") {
+ assert_false(eventName in eventsWithFractions,
+ eventName + " should not have fractional coordinates");
+ } else {
+ assert_true(eventName in eventsWithFractions,
+ eventName + " should have fractional coordinates");
+ }
+ });
+ // At this point, we know that `eventsWithFractions` contains all
+ // of `eventList` except "click". The assert below rules out any
+ // extra entry in `eventsWithFractions`.
+ assert_equals(Object.keys(eventsWithFractions).length,
+ eventList.length - 1,
+ "eventsWithFractions list does not have any redundant entry");
+ }, expectedPointerType);
+ test_pointerEvent.done();
+ }
+ });
+ });
+
+ testInputType(input);
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events coordinates support fractional value</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks pointer events has fractional client coordinates
+ <ol>
+ <li>Move your pointer over one black square</li>
+ <li>Press down the pointer (i.e. press left button with mouse or touch the screen with finger or pen).</li>
+ <li>Release the pointer.</li>
+ <li>Move to next black square and repreat 2 and 3</li>
+ </ol>
+
+ Test passes if pointer events has fractional coordinates.
+ </h4>
+ <iframe id=innerFrame src="resources/pointerevent_fractional_coordinates-iframe.html"></iframe>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll.html b/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll.html
new file mode 100644
index 0000000000..6465349849
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: none correctly targets scrolls</title>
+<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>
+<style>
+ body {
+ height: 200vh;
+ }
+
+ #overlay {
+ overflow: auto;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ }
+
+ #scrollable {
+ background: white;
+ border: 3px solid green;
+ height: 150px;
+ /* pointer-events: auto; should allow scrolling over this element. */
+ pointer-events: auto;
+ }
+
+ #spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<body id="body">
+ <div id="overlay">
+ <div id="scrollable"></div>
+ <div id="spacer"></div>
+ </div>
+</body>
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("overlay")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 250, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "root", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: none scroller skips that scroller");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll_visible_descendant.html b/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll_visible_descendant.html
new file mode 100644
index 0000000000..0a09137e76
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_hit_test_scroll_visible_descendant.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>pointer-events: auto descendant correctly targets scrolls</title>
+<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>
+<style>
+ body {
+ height: 200vh;
+ }
+
+ #overlay {
+ overflow: auto;
+ height: 300px;
+ border: 2px solid blue;
+ pointer-events: none;
+ }
+
+ #scrollable {
+ background: white;
+ border: 3px solid green;
+ height: 150px;
+ /* pointer-events: auto; should allow scrolling over this element. */
+ pointer-events: auto;
+ }
+
+ #spacer {
+ height: 200vh;
+ }
+
+</style>
+
+<body id="body">
+ <div id="overlay">
+ <div id="scrollable"></div>
+ <div id="spacer"></div>
+ </div>
+</body>
+<script>
+ promise_test(async (t) => {
+ let scrolled = new Promise((resolve) => {
+ let scrollers = [window, document.getElementById("overlay")];
+ let onscroll = (evt) => {
+ for (const scroller of scrollers) {
+ scroller.removeEventListener("scroll", onscroll);
+ }
+ resolve(evt.target.id || "root");
+ }
+ for (const scroller of scrollers) {
+ scroller.addEventListener("scroll", onscroll);
+ }
+ });
+ const actions = new test_driver.Actions().scroll(50, 50, 0, 50, { duration: 50 });
+ actions.send();
+ assert_equals(await scrolled, "overlay", "Incorrect element scrolled");
+ }, "Wheel-scroll over pointer-events: auto descendant scrolls pointer-events: none scroller.");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_iframe-touch-action-none_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_iframe-touch-action-none_touch.html
new file mode 100644
index 0000000000..109aec8dcd
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_iframe-touch-action-none_touch.html
@@ -0,0 +1,66 @@
+<html>
+ <head>
+ <title>iframe touch-action:none attribute</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>iframe touch-action:none attribute</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>Test Description: Drag your finger in the green rectangle below then tap the "Done" button.</h4>
+ <br>
+ <iframe id="target"
+ style="touch-action:none;"
+ src="resources/iframe-touch-action-none-subframe.html">
+ </iframe>
+ <input type="button" id="btnDone" value="Done">
+ </body>
+ <script>
+ var event_log = [];
+
+ function resetTestState() {
+ event_log = [];
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("iframe received pointercancel",
+ ["touch"]);
+ var actions_promise;
+ var target = document.getElementById("target");
+
+ on_event(document.getElementById("btnDone"), "click", () => {
+ test_pointerEvent.step(() => {
+ assert_equals(event_log.join(", "), "pointercancel",
+ "exactly one pointercancel received");
+ });
+ actions_promise.then(() => test_pointerEvent.done());
+ });
+
+ on_event(window, "message", function(event) {
+ if (event.source != target.contentWindow) {
+ return;
+ }
+ if (event.data && event.data.type == "subframe-event") {
+ event_log.push(event.data.eventType);
+ }
+ });
+
+ actions_promise = new test_driver.Actions()
+ .addPointer("pointer1", "touch")
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(25, 25, {origin: target})
+ .pointerUp()
+ .pointerMove(0, 0, {origin: btnDone})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html
new file mode 100644
index 0000000000..d2fa87d333
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html
@@ -0,0 +1,120 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Lostpointercapture fires on document when target is removed</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events - lostpointercapture when capturing element is removed</h1>
+ <div id="target0"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var isDisconnected = false;
+ var lostPointerCapture = false;
+ var count = 0;
+ var event_log = [];
+ var recieved_before_lostcapture = [];
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var captureButton = document.getElementById('btnCapture');
+
+ var test_lostpointercapture = async_test("lostpointercapture event received");
+
+ window.onload = function() {
+ var actions_promise;
+
+ const POINTER_EVENT_TYPES = ['pointerover', 'pointerenter', 'pointerdown', 'pointermove', 'pointerup', 'pointercancel', 'pointerout', 'pointerleave'];
+ // Add listeners to target0 explicitly as it is removed from the DOM.
+ for (let target of [document.body, target0]) {
+ for (let eventType of POINTER_EVENT_TYPES) {
+ target.addEventListener(eventType, (evt) => {
+ // Prevent double-reporting events dispatched to target0.
+ if (target == document.body && evt.target == target0) {
+ return;
+ }
+ const ident = evt.target.id || evt.target.tagName;
+ event_log.push(`${evt.type}@${ident}`);
+ if (isDisconnected && !lostPointerCapture) {
+ recieved_before_lostcapture.push(`${evt.type}@${ident}`);
+ }
+ });
+ }
+ }
+
+ on_event(captureButton, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ target0.setPointerCapture(event.pointerId);
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ lostPointerCapture = false;
+ event_log.push('gotpointercapture@target0');
+ isDisconnected = true;
+ received_events = [];
+ target0.parentNode.removeChild(target0);
+ test(function() {
+ assert_true(!lostPointerCapture, "lostpointercapture must not be fired synchronously with DOM removal");
+ });
+ });
+
+
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ lostPointerCapture = true;
+ test(function() {
+ // TA: 11.3
+ assert_unreached("lostpointercapture must be fired on the document, not the capturing element");
+ }, "lostpointercapture must not be dispatched on the disconnected node");
+ });
+
+ on_event(document, 'lostpointercapture', function(e) {
+ lostPointerCapture = true;
+ test(function() {
+ assert_equals(recieved_before_lostcapture.join(", "), "", "No pointerevents should be received before lost capture is resolved");
+ }, "lostpointercapture must be received before the next pointerevent after the node is disconnected");
+ event_log.push('lostpointercapture@document');
+ test(function() {
+ // TA: 11.3
+ assert_true(isDisconnected, "lostpointercapture must be fired on the document");
+ }, "lostpointercapture is dispatched on the document");
+
+ actions_promise.then( () => {
+ test_lostpointercapture.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(2, 0, {origin: captureButton})
+ .pointerMove(5, 0, {origin: captureButton})
+ .pointerMove(8, 0, {origin: captureButton})
+ .pointerMove(10, 0, {origin: captureButton})
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html
new file mode 100644
index 0000000000..f92daeb02e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Lostpointercapture fires on document when target in shadow dom is removed</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+ <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>
+ <body onload="onLoad()">
+ <template id="template">
+ <style>
+ #content{
+ height:100px;
+ width:100px;
+ background-color: lightgrey;
+ }
+ </style>
+ <div id="content"></div>
+ </template>
+ <h1>Pointer Events - lostpointercapture when capturing element in shadow dom is removed</h1>
+ <h4>
+ Test Description:
+ This test checks if lostpointercapture is fired at the document when the capturing node that is in shadow dom is removed from the shadow dom.
+ Complete the following actions:
+ <ol>
+ <li>Press left mouse button over "Set Capture" button. Pointer should be captured by the gray rectangle which is in shadow dom.</li>
+ <li>Gray rectangle will be removed from shadow dom.</li>
+ <li>"lostpointercapture" should be received on the document not on the gray rectangle.</li>
+ </ol>
+ </h4>
+ <div id="shadowhost"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <div id="log"></div>
+ <script>
+ async function onLoad(){
+ var logDiv = document.getElementById("log");
+ function logMessage(message){
+ var log = document.createElement("div");
+ var messageNode = document.createTextNode(message);
+ log.appendChild(messageNode);
+ logDiv.appendChild(log);
+ }
+ var events = [];
+
+ var host = document.getElementById("shadowhost");
+ var shadowRoot = host.attachShadow({mode: "open"});
+ var template = document.getElementById("template");
+ var node = template.content.cloneNode(true);
+ shadowRoot.appendChild(node);
+
+ var content = host.shadowRoot.getElementById("content");
+ var captureButton = document.getElementById("btnCapture");
+
+ captureButton.addEventListener("pointerdown", function(event){
+ logMessage("Pointer will be captured by the shadow dom gray rectangle.");
+ content.setPointerCapture(event.pointerId);
+ events.push("pointerdown@captureButton");
+ });
+ content.addEventListener("gotpointercapture", function(event){
+ logMessage("Gray rectangle received pointercapture.");
+ logMessage("Removing gray rectangle from shadow dom.")
+ content.parentNode.removeChild(content);
+ events.push("gotpointercapture@content");
+ });
+ content.addEventListener("lostpointercapture", function(event){
+ logMessage("Test Failed! The element removed from shadow dom should not receive lostpointercapture.")
+ events.push("lostpointercapture@content");
+ if(window.promise_test && shadow_dom_test){
+ shadow_dom_test.step(function(){
+ assert_unreached("lostpointercapture must be fired on the document, not the capturing element");
+ reject_test("lostpointercapture must not be dispatched on the disconnected node");
+ shadow_dom_test.done();
+ });
+ }
+ });
+ document.addEventListener("lostpointercapture", function(event){
+ logMessage("Test Passed! Document received lostpointercapture.");
+ events.push("lostpointercapture@document");
+ if(window.promise_test && shadow_dom_test){
+ shadow_dom_test.step(function(){
+ assert_array_equals(events, ["pointerdown@captureButton",
+ "gotpointercapture@content",
+ "lostpointercapture@document"]);
+ resolve_test();
+ });
+ }
+ });
+
+ var shadow_dom_test = null;
+ var resolve_test = null;
+ var reject_test = null;
+
+ function cleanup(){
+ events = [];
+ shadow_dom_test = null;
+ resolve_test = null;
+ reject_test = null;
+ }
+ if(window.promise_test){
+ promise_test(async function(t){
+ var actions_promise;
+ return new Promise(async function(resolve, reject){
+ shadow_dom_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ t.add_cleanup(function(){
+ cleanup();
+ });
+ var actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(0, 0, {origin:captureButton})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }).finally(async () => {
+ await actions_promise;
+ t.done();
+ });
+ }, "lostpointercapture is dispatched on the document when shadow dom capturing element is removed");
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_shadow_host.html b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_shadow_host.html
new file mode 100644
index 0000000000..26fa992f76
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_shadow_host.html
@@ -0,0 +1,138 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Lostpointercapture fires on document when target in shadow dom is removed</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+ <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>
+ #shadowhost{
+ height:200px;
+ width:200px;
+ background-color:magenta;
+ }
+ </style>
+ </head>
+ <body onload="onLoad()">
+ <template id="template">
+ <style>
+ #content{
+ height:100px;
+ width:100px;
+ background-color: lightgrey;
+ }
+ </style>
+ <div id="content"></div>
+ </template>
+ <h1>Pointer Events - lostpointercapture when capturing element in shadow dom is removed by removing the shadow host</h1>
+ <h4>
+ Test Description:
+ This test checks if lostpointercapture is fired at the document when the capturing node is removed from the document by removing the shadow host.
+ The shadow host is colored magenta and the shadow dom element is colored gray.
+ Complete the following actions:
+ <ol>
+ <li>Press left mouse button over "Set Capture" button. Pointer should be captured by the gray rectangle.</li>
+ <li>Shadow host magenta rectangle including the gray rectangle will be removed from shadow dom.</li>
+ <li>"lostpointercapture" should be received on the document not on the gray rectangle.</li>
+ </ol>
+ </h4>
+ <div id="shadowhost"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <div id="log"></div>
+ <script>
+ async function onLoad(){
+ var logDiv = document.getElementById("log");
+ function logMessage(message){
+ var log = document.createElement("div");
+ var messageNode = document.createTextNode(message);
+ log.appendChild(messageNode);
+ logDiv.appendChild(log);
+ }
+ var events = [];
+
+ var host = document.getElementById("shadowhost");
+ var shadowRoot = host.attachShadow({mode: "open"});
+ var template = document.getElementById("template");
+ var node = template.content.cloneNode(true);
+ shadowRoot.appendChild(node);
+
+ var content = host.shadowRoot.getElementById("content");
+ var captureButton = document.getElementById("btnCapture");
+
+ captureButton.addEventListener("pointerdown", function(event){
+ logMessage("Pointer will be captured by the shadow dom gray rectangle.");
+ content.setPointerCapture(event.pointerId);
+ events.push("pointerdown@captureButton");
+ });
+ content.addEventListener("gotpointercapture", function(event){
+ logMessage("Gray rectangle received pointercapture.");
+ logMessage("Removing magenta rectangle (which includes gray rectangle) from shadow dom.")
+ host.parentNode.removeChild(host);
+ events.push("gotpointercapture@content");
+ });
+ content.addEventListener("lostpointercapture", function(event){
+ logMessage("Test Failed! The element removed from shadow dom should not receive lostpointercapture.")
+ events.push("lostpointercapture@content");
+ if(window.promise_test && shadow_dom_test){
+ shadow_dom_test.step(function(){
+ assert_unreached("lostpointercapture must be fired on the document, not the capturing element");
+ reject_test("lostpointercapture must not be dispatched on the disconnected node");
+ shadow_dom_test.done();
+ });
+ }
+ });
+ document.addEventListener("lostpointercapture", function(event){
+ logMessage("Test Passed! Document received lostpointercapture.");
+ events.push("lostpointercapture@document");
+ if(window.promise_test && shadow_dom_test){
+ shadow_dom_test.step(function(){
+ assert_array_equals(events, ["pointerdown@captureButton",
+ "gotpointercapture@content",
+ "lostpointercapture@document"]);
+ resolve_test();
+ });
+ }
+ });
+
+ var shadow_dom_test = null;
+ var resolve_test = null;
+ var reject_test = null;
+
+ function cleanup(){
+ events = [];
+ shadow_dom_test = null;
+ resolve_test = null;
+ reject_test = null;
+ }
+ if(window.promise_test){
+ promise_test(async function(t){
+ var actions_promise;
+ return new Promise(function(resolve, reject){
+ shadow_dom_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ t.add_cleanup(function(){
+ cleanup();
+ });
+ var actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(0, 0, {origin:captureButton})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }).finally(async () => {
+ await actions_promise;
+ t.done();
+ });
+ }, "lostpointercapture is dispatched on the document when shadow host is removed");
+ }
+ }
+ </script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_is_first.html b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_is_first.html
new file mode 100644
index 0000000000..c84961b6c5
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_is_first.html
@@ -0,0 +1,151 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Lostpointercapture triggers first and asynchronously</title>
+ <meta name="assert" content="TA5.2.10: A user agent must fire a pointer event named lostpointercapture after pointer capture is released for a pointer. This event must be fired prior to any subsequent events for the pointer after capture was released. This event is fired at the element from which pointer capture was removed;">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events capture test - lostpointercapture order</h1>
+ <h4>
+ Test Description:
+ This test checks if lostpointercapture is handled asynchronously and prior to all subsequent events.
+ Complete the following actions:
+ <ol>
+ <li>Press and hold left mouse button over "Set Capture" button and move a little. "gotpointercapture" should be logged inside of the black rectangle
+ <li>"lostpointercapture" should be logged inside of the black rectangle after pointerup
+ </ol>
+ </h4>
+ Test passes if lostpointercapture is dispatched after releasing the mouse button and before any additional pointer events.
+ <div id="target0" style="background:black; color:white"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var detected_pointerEvents = new Array();
+ var pointerdown_event = null;
+ var firstPointermoveReceived = false; // To handle the first pointermove right after gotpointercapture which gotpointercapture was sent right before it.
+ var firstPointeroverReceived = false; // To handle the first pointerover right after gotpointercapture which gotpointercapture was sent right before it.
+ var firstPointerenterReceived = false; // To handle the first pointeenter right after gotpointercapture which gotpointercapture was sent right before it.
+
+ var test_pointerEvent = async_test("lostpointercapture is dispatched prior to subsequent events"); // set up test harness
+
+ var isPointerCapture = false;
+ var count=0;
+
+ var testStarted = false;
+ var eventRcvd = false;
+ var isAsync = false;
+ var event_log = [];
+ var actions_promise;
+
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var captureButton = document.getElementById('btnCapture');
+
+ function run() {
+ on_event(captureButton, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ pointerdown_event = event;
+ if(isPointerCapture == false) {
+ isPointerCapture = true;
+ captureButton.value = 'Release Capture';
+ sPointerCapture(event);
+ }
+ });
+
+ // TA5.1.3.1: Process Pending Pointer Capture
+ // Whenever a user agent is to fire a Pointer Event that is not gotpointercapture or lostpointercapture,
+ // it must first run the steps of processing pending pointer capture
+ //
+ // TA5.2.12: The lostpointercapture event
+ // After pointer capture is released for a pointer, and prior to any subsequent events for the pointer,
+ // the lostpointercapture event must be dispatched to the element from which pointer capture was removed.
+ // listen to all events
+ for (var i = 0; i < All_Pointer_Events.length; i++) {
+ on_event(target0, All_Pointer_Events[i], function (event) {
+ // if the event was gotpointercapture, just log it and return
+ if (event.type == "gotpointercapture") {
+ testStarted = true;
+ rPointerCapture(event);
+ isAsync = true;
+ event_log.push('gotpointercapture@target0');
+ return;
+ }
+ else if (event.type == "lostpointercapture") {
+ event_log.push('lostpointercapture@target0');
+ captureButton.value = 'Set Capture';
+ isPointerCapture = false;
+
+ // TA: 11.2
+ test_pointerEvent.step(function () {
+ assert_true(isAsync, "lostpointercapture must be fired asynchronously");
+ });
+
+ // if any events except pointerup have been received with same pointerId before lostpointercapture, then fail
+ var eventsRcvd_str = "";
+ if (eventRcvd) {
+ eventsRcvd_str = "Events received before lostpointercapture: ";
+ for (var i = 0; i < detected_pointerEvents.length; i++) {
+ eventsRcvd_str += detected_pointerEvents[i] + ", ";
+ }
+ }
+ test_pointerEvent.step(function () {
+ assert_false(eventRcvd, "no other events should be received before lostpointercapture." + eventsRcvd_str);
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerID is same for pointerdown and lostpointercapture");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+ else {
+ if (testStarted && pointerdown_event != null && pointerdown_event.pointerId === event.pointerId) {
+ if (event.type == "pointermove" && !firstPointermoveReceived) {
+ firstPointermoveReceived = true;
+ }
+ else if (event.type == "pointerenter" && !firstPointerenterReceived) {
+ firstPointerenterReceived = true;
+ }
+ else if (event.type == "pointerover" && !firstPointeroverReceived) {
+ firstPointeroverReceived = true;
+ }
+ else if (event.type != "pointerup") {
+ detected_pointerEvents.push(event.type);
+ eventRcvd = true;
+ }
+ }
+ }
+ });
+ }
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(2, 0, {origin: captureButton})
+ .pointerMove(5, 0, {origin: captureButton})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_remove_setcapture_node.html b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_remove_setcapture_node.html
new file mode 100644
index 0000000000..4b372d04bd
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_lostpointercapture_remove_setcapture_node.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Lostpointercapture fires on document when target is removed</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events - lostpointercapture removes new capture element</h1>
+ <input type="button" id="button" value="Set Capture"><br>
+ <div id="target0"></div>
+ <div id="target1"></div>
+ <script type='text/javascript'>
+ var target0 = document.getElementById('target0');
+ var target1 = document.getElementById('target1');
+ var captureButton = document.getElementById('button');
+ var targets = [target0, target1, captureButton];
+ const LOG_EVENT_TYPES = ['pointerover', 'pointerenter', 'pointerdown', 'pointermove', 'pointerup', 'pointercancel', 'pointerout', 'pointerleave', 'gotpointercapture', 'lostpointercapture'];
+ promise_test(async (test) => {
+ captureButton.focus();
+ var events = [];
+ var lastLogMessage = "";
+ let logEvent = (event) => {
+ let logMessage = `${event.type}@${event.target.id || event.target.tagName || 'document'}`;
+ // Only log a particular event once to avoid coalescing differences.
+ if (logMessage == lastLogMessage)
+ return;
+ lastLogMessage = logMessage;
+ events.push(logMessage);
+ };
+ document.addEventListener('lostpointercapture', logEvent);
+ for (const target of targets) {
+ for (const eventType of LOG_EVENT_TYPES) {
+ target.addEventListener(eventType, logEvent);
+ }
+ }
+ const nextSibling = target1.nextSibling;
+ test.add_cleanup(() => {
+ document.removeEventListener('lostpointercapture', logEvent);
+ for (const target of targets) {
+ for (const eventType of LOG_EVENT_TYPES) {
+ target.removeEventListener(eventType, logEvent);
+ }
+ }
+ nextSibling.parentNode.insertBefore(target1, nextSibling);
+ });
+
+ let finishPromise = Promise.any([
+ getEvent('pointerup', document.body, test),
+ getEvent('pointerup', target1, test)]);
+
+ getEvent('pointerdown', captureButton, test).then((event) => {
+ target0.setPointerCapture(event.pointerId);
+ });
+ // On the first captured move, we'll set capture to target1.
+ getEvent('pointermove', target0, test).then((event) => {
+ target1.setPointerCapture(event.pointerId);
+ });
+ // But remove the new capture target when we lose capture.
+ getEvent('lostpointercapture', target0, test).then((event) => {
+ target1.remove();
+ });
+ getEvent('gotpointercapture', target1, test).then((event) => {
+ assert_unreached("target1 is removed and should never get pointer capture.");
+ });
+
+ // Inject mouse inputs.
+ const actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: captureButton})
+ .pointerUp()
+ .send();
+
+ await finishPromise;
+
+ assert_equals(events.join(", "), [
+ // Pointer down on button
+ "pointerover@button", "pointerenter@button", "pointermove@button", "pointerdown@button",
+ // Captured by target0
+ "pointerout@button", "pointerleave@button", "pointerover@target0", "pointerenter@target0", "gotpointercapture@target0", "pointermove@target0",
+ // Captured by target1, losing capture on target0 which removes target1.
+ "lostpointercapture@target0", "pointerout@target0", "pointerleave@target0",
+ // Uncaptured pointer re-enters button and is lifted.
+ "pointerover@button", "pointerenter@button", "pointermove@button", "pointerup@button"
+ ].join(", "));
+ }, "setPointerCapture target removed by lostpointercapture");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_mouse_capture_change_hover.html b/testing/web-platform/tests/pointerevents/pointerevent_mouse_capture_change_hover.html
new file mode 100644
index 0000000000..ef824dafd9
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_mouse_capture_change_hover.html
@@ -0,0 +1,170 @@
+<!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>
+<style>
+body {
+ user-select: none;
+}
+#green:hover {
+ background-color: red;
+}
+#blue:hover {
+ background-color: red;
+}
+#green {
+ background-color: green;
+}
+#blue {
+ background-color: blue;
+}
+div.box {
+ margin: 5px;
+ padding: 20px;
+ width: 50px;
+ height: 50px;
+}
+</style>
+<body onload="run()">
+ <div id="green" class="box"></div>
+ <div id="blue" class="box"></div>
+</body>
+<script>
+var receivedEventList = [];
+var setcapture = "";
+
+let testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture', "pointerover", "pointerout", "pointerleave", "pointerenter"];
+testEventList.forEach(function(eventName) {
+ green.addEventListener(eventName, logEvent);
+ blue.addEventListener(eventName, logEvent);
+});
+
+function logEvent(event) {
+ receivedEventList.push(event.target.id + " received " + event.type)
+}
+
+function setCaptureGreen(event) {
+ green.setPointerCapture(event.pointerId);
+}
+
+function setCaptureBlue(event) {
+ blue.setPointerCapture(event.pointerId);
+}
+
+function releaseCapture(event) {
+ if (event.target.hasPointerCapture(event.pointerId)) {
+ event.target.releasePointerCapture(event.pointerId);
+ }
+}
+
+function run() {
+ promise_test (async() => {
+ // Move to (0, 0) to reset hovering.
+ await new test_driver.Actions().pointerMove(0, 0).send();
+ receivedEventList = [];
+
+ // pointerdown at green -> set capture to green -> green receive the following moves.
+ document.addEventListener("pointerdown", setCaptureGreen);
+
+ await new test_driver.Actions()
+ .addPointer("mouse")
+ .pointerMove(25, 25, {origin: green})
+ .pointerDown()
+ .pointerMove(30, 30, {origin: green})
+ .pointerMove(25, 25, {origin: blue})
+ .send();
+
+ expectedEventList = ["green received pointerover",
+ "green received pointerenter",
+ "green received pointermove",
+ "green received pointerdown",
+ "green received gotpointercapture",
+ "green received pointermove",
+ "green received pointermove"];
+
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ assert_equals(getComputedStyle(green).backgroundColor, "rgb(255, 0, 0)", "green should be red (hover).");
+ assert_equals(getComputedStyle(blue).backgroundColor, "rgb(0, 0, 255)", "blue should be blue.");
+ document.removeEventListener("pointerdown", setCaptureGreen);
+ // Release mouse button.
+ await new test_driver.Actions().addPointer("mouse").pointerUp().send();
+ }, "Mouse down and capture to green.");
+
+ promise_test (async() => {
+ // Move to (0, 0) to reset hovering.
+ await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0).send();
+ receivedEventList = [];
+
+ // pointerdown at green -> set capture to blue -> blue receive the following moves.
+ document.addEventListener("pointerdown", setCaptureBlue);
+
+ await new test_driver.Actions()
+ .addPointer("mouse")
+ .pointerMove(25, 25, {origin: green})
+ .pointerDown()
+ .pointerMove(30, 30, {origin: green})
+ .pointerMove(31, 31, {origin: green})
+ .send();
+
+ expectedEventList = ["green received pointerover",
+ "green received pointerenter",
+ "green received pointermove",
+ "green received pointerdown",
+ "green received pointerout",
+ "green received pointerleave",
+ "blue received pointerover",
+ "blue received pointerenter",
+ "blue received gotpointercapture",
+ "blue received pointermove",
+ "blue received pointermove"];
+
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ assert_equals(getComputedStyle(green).backgroundColor, "rgb(0, 128, 0)", "green should be green.");
+ assert_equals(getComputedStyle(blue).backgroundColor, "rgb(255, 0, 0)", "blue should be red (hover).");
+ document.removeEventListener("pointerdown", setCaptureBlue);
+ // Release mouse button.
+ await new test_driver.Actions().addPointer("mouse").pointerUp().send();
+ }, "Mouse down at green and capture to blue.");
+
+ promise_test (async() => {
+ // Move to (0, 0) to reset hovering.
+ await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0).send();
+ receivedEventList = [];
+
+ // pointerdown at green -> set capture to green -> green receive first move -> release capture -> blue receive the next move
+ green.addEventListener("pointerdown", setCaptureGreen);
+ green.addEventListener("pointermove", releaseCapture);
+
+ await new test_driver.Actions()
+ .addPointer("mouse")
+ .pointerMove(25, 25, {origin: green})
+ .pointerDown()
+ .pointerMove(30, 30, {origin: blue})
+ .pointerMove(25, 25, {origin: blue})
+ .send();
+
+ expectedEventList = ["green received pointerover",
+ "green received pointerenter",
+ "green received pointermove",
+ "green received pointerdown",
+ "green received gotpointercapture",
+ "green received pointermove",
+ "green received lostpointercapture",
+ "green received pointerout",
+ "green received pointerleave",
+ "blue received pointerover",
+ "blue received pointerenter",
+ "blue received pointermove"]
+
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ assert_equals(getComputedStyle(green).backgroundColor, "rgb(0, 128, 0)", "green should be green.");
+ assert_equals(getComputedStyle(blue).backgroundColor, "rgb(255, 0, 0)", "blue should be red (hover).");
+ green.removeEventListener("pointerdown", setCaptureBlue);
+ green.removeEventListener("pointermove", releaseCapture);
+ // Release mouse button.
+ await new test_driver.Actions().addPointer("mouse").pointerUp().send();
+ }, "Mouse down and capture to green, move to blue and release capture");
+}
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html b/testing/web-platform/tests/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
new file mode 100644
index 0000000000..24b65213ec
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
@@ -0,0 +1,82 @@
+<!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>
+<style>
+ iframe {
+ width: 300px;
+ height: 300px;
+ top: 100px;
+ left: 100px;
+ border: 0;
+ position: absolute;
+ background: green;
+ }
+ #outerFrame {
+ width: 500px;
+ height: 500px;
+ background: blue;
+ }
+</style>
+<body onload="run()">
+ <div id='outerFrame'>
+ <iframe id='iframe' src="resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html"></iframe>
+ </div>
+</body>
+
+<script type="text/javascript">
+ function run() {
+ capture_count = 0;
+
+ document.addEventListener("gotpointercapture", function(){
+ capture_count ++;
+ })
+
+ iframe.contentDocument.addEventListener("gotpointercapture", function(){
+ capture_count ++;
+ })
+
+ document.addEventListener("pointerdown", function(event){
+ // Outer frame got pointer down, set capture to inner frame.
+ iframe.contentDocument.getElementById("target").setPointerCapture(event.pointerId);
+ });
+
+
+ iframe.contentDocument.addEventListener("pointerdown", function(event){
+ // Inner frame got pointer down, set capture to outer frame.
+ outerFrame.setPointerCapture(event.pointerId);
+ });
+
+
+ promise_test(async(test) => {
+ let eventWatcher = new EventWatcher(test, document, ["pointerup"]);
+ let donePromise = eventWatcher.wait_for(["pointerup"], { record: 'all' });
+ await injectEvent(50, 50);
+ // wait for pointerUp before running the test
+ await donePromise;
+ assert_equals(capture_count, 0, "Inner frame can not capture when pointer is activate in outer frame")
+ }, "setPointerCapture: pointer active in outer frame, set capture to inner frame");
+
+
+ promise_test(async(test) => {
+ let eventWatcher = new EventWatcher(test, iframe.contentDocument, ["pointerup"]);
+ let donePromise = eventWatcher.wait_for(["pointerup"], { record: 'all' });
+ await injectEvent(250, 250);
+ // wait for pointerUp before running the test
+ await donePromise;
+ assert_equals(capture_count, 0, "Outer frame can not capture when pointer is activate in inner frame")
+ }, "setPointerCapture: pointer active in inner frame, set capture to outer frame");
+
+ function injectEvent(x, y) {
+ return new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerMove(x, y)
+ .pointerUp()
+ .send();
+ }
+}
+</script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_movementxy.html b/testing/web-platform/tests/pointerevents/pointerevent_movementxy.html
new file mode 100644
index 0000000000..cb69c5c95e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_movementxy.html
@@ -0,0 +1,152 @@
+<!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 actionsPromise;
+
+ function resetTestState() {
+ lastScreenX = null;
+ lastScreenY = null;
+ }
+
+ 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()
+ .pointerMove(20, 30, {origin: box1})
+ .pointerMove(50, 40, {origin: box1})
+ .pointerMove(80, 30, {origin: box1})
+ .pointerMove(110, 20, {origin: box1})
+ .pointerMove(0, 0, {origin: box2})
+ .pointerUp()
+ .send();
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", [inputSource]);
+
+ [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.");
+ });
+ // Reset when entering the new frame.
+ if (event.type == "pointerenter") {
+ lastScreenX = null;
+ lastScreenY = null;
+ }
+ }
+ });
+ });
+
+ on_event(element, 'pointermove', function (event) {
+ test_pointerEvent.step(function() {
+ if (lastScreenX && lastScreenY) {
+ 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;
+ });
+ });
+ on_event(document.querySelector('#box1'), 'pointerdown', function(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;
+ });
+ on_event(document.querySelector('#box2'), 'pointerup', function(event) {
+ // Make sure the test finishes after all the input actions are completed.
+ 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" src="resources/pointerevent_movementxy-iframe.html"></iframe>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_multiple_primary_pointers_boundary_events-manual.html b/testing/web-platform/tests/pointerevents/pointerevent_multiple_primary_pointers_boundary_events-manual.html
new file mode 100644
index 0000000000..029aa26368
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_multiple_primary_pointers_boundary_events-manual.html
@@ -0,0 +1,145 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Boundary compatibility events for multiple primary pointers</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var test_pointerEvent = async_test("Multi-pointer boundary compat events");
+ add_completion_callback(end_of_test);
+
+ var detected_pointertypes = {};
+ var event_log = [];
+
+ // These two ids help us detect two different pointing devices.
+ var first_entry_pointer_id = -1;
+ var second_entry_pointer_id = -1;
+
+ // Current node for each pointer id
+ var current_node_for_id = {};
+
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ function end_of_interaction() {
+ test(function () {
+ assert_equals(event_log.join(", "),
+ "mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0, " +
+ "mouseover@target1, mouseenter@target1, mouseout@target1, mouseleave@target1, " +
+ "mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0"
+ );
+ }, "Event log");
+
+ test_pointerEvent.done(); // complete test
+ }
+
+ function log_event(label) {
+ event_log.push(label);
+ }
+
+ function run() {
+ on_event(document.getElementById("done"), "click", end_of_interaction);
+
+ var target_list = ["target0", "target1"];
+ var pointer_event_list = ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown"];
+ var mouse_event_list = ["mouseenter", "mouseleave", "mouseover", "mouseout"];
+
+ target_list.forEach(function(targetId) {
+ var target = document.getElementById(targetId);
+
+ pointer_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ var label = event.type + "@" + targetId;
+
+ detected_pointertypes[event.pointerType] = true;
+
+ if (!event.isPrimary) {
+ test(function () {
+ assert_unreached("Non-primary pointer " + label);
+ }, "Non-primary pointer " + label);
+ }
+
+ if (event.type === "pointerenter") {
+ var pointer_id = event.pointerId;
+ if (current_node_for_id[pointer_id] !== undefined) {
+ test(function () {
+ assert_unreached("Double entry " + label);
+ }, "Double entry " + label);
+ }
+ current_node_for_id[pointer_id] = event.target;
+
+ // Test that two different pointing devices are used
+ if (first_entry_pointer_id === -1) {
+ first_entry_pointer_id = pointer_id;
+ } else if (second_entry_pointer_id === -1) {
+ second_entry_pointer_id = pointer_id;
+ test(function () {
+ assert_not_equals(first_entry_pointer_id, second_entry_pointer_id);
+ }, "Different pointing devices");
+ }
+ } else if (event.type === "pointerleave") {
+ var pointer_id = event.pointerId;
+ if (current_node_for_id[pointer_id] !== event.target) {
+ test(function () {
+ assert_unreached("Double exit " + label);
+ }, "Double exit " + label);
+ }
+ current_node_for_id[pointer_id] = undefined;
+ }
+ });
+ });
+
+ mouse_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ log_event(event.type + "@" + targetId);
+ });
+ });
+ });
+ }
+ </script>
+ <style>
+ #target0, #target1 {
+ margin: 20px;
+ }
+
+ #done {
+ margin: 20px;
+ border: 2px solid black;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Boundary compatibility events for multiple primary pointers</h1>
+ <h4>
+ When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence.
+ </h4>
+ Instruction:
+ <ol>
+ <li>Move the mouse directly into Target0 (without going through Target1), and then leave the mouse there unmoved.</li>
+ <li>Tap directly on Target1 with a finger or a stylus, and then lift the finger/stylus off the screen/digitizer without crossing Target1 boundary.</li>
+ <li>Move the mouse into Target0 (if not there already) and move inside it.</li>
+ <li>Click Done (without passing over Target1).</li>
+ </ol>
+ <div id="done">
+ Done
+ </div>
+ <div id="target0">
+ Target0
+ </div>
+ <div id="target1">
+ Target1
+ </div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_on_event_handlers.html b/testing/web-platform/tests/pointerevents/pointerevent_on_event_handlers.html
new file mode 100644
index 0000000000..d8cfa7a0f4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_on_event_handlers.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerEvent: Constructor test</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>PointerEvent: Dispatch custom event</h1>
+ <h4>Test Description: This test checks if on pointer event handlers through attributes works properly using synthetic pointerevents. For valid results, this test must be run without generating real (trusted) events on the black rectangle below.</h4>
+ <div id="target0"
+ onpointercancel="window.eventHappened = 'pointercancel';"
+ onpointerdown="window.eventHappened = 'pointerdown';"
+ onpointerup="window.eventHappened = 'pointerup';"
+ onpointermove="window.eventHappened = 'pointermove';"
+ onpointerout="window.eventHappened = 'pointerout';"
+ onpointerover="window.eventHappened = 'pointerover';"
+ onpointerleave="window.eventHappened = 'pointerleave';"
+ onpointerenter="window.eventHappened = 'pointerenter';"
+ ongotpointercapture="window.eventHappened = 'gotpointercapture';"
+ onlostpointercapture="window.eventHappened = 'lostpointercapture';"
+ ></div>
+ <script>
+ window.eventHappened = '';
+
+ All_Pointer_Events.forEach(function(event) {
+ var on_event = "on" + event;
+
+ test(function() {
+ const htmlElement = document.createElement("span");
+ const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
+ for (var location of [window, htmlElement, svgElement, document]) {
+ assert_equals(location[on_event], null,
+ `The default value of the property is null for a ${location.constructor.name} instance`);
+ }
+ }, "The default value of " + on_event + " is always null");
+
+ test(function() {
+ window.eventHappened = '';
+ const element = document.querySelector("#target0");
+ const compiledHandler = element[on_event];
+ assert_equals(typeof compiledHandler, "function", "The " + on_event + " property must be a function");
+ compiledHandler();
+ assert_equals(window.eventHappened, event, "Calling the handler must run the code");
+ }, "The " + on_event + " content attribute must be compiled into the " + on_event + " property");
+
+ var handlerTest = async_test("dispatching a " + event + " event must trigger element." + on_event);
+ const element = document.createElement("meta");
+ element[on_event] = function(e) {
+ handlerTest.step(function() {
+ assert_equals(e.currentTarget, element, "The event must be fired at the <meta> element");
+ });
+ handlerTest.done();
+ };
+ element.dispatchEvent(new Event(event));
+ });
+ </script>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerId_scope.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerId_scope.html
new file mode 100644
index 0000000000..e3651b9d34
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerId_scope.html
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+ <!--
+Test cases for Pointer Events v1 spec
+This document references Test Assertions (abbrev TA below) written by Cathy Chan
+http://www.w3.org/wiki/PointerEvents/TestAssertions
+-->
+ <head>
+ <title>Pointer Events pointerdown tests</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerId of an active pointer is the same across iframes");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+ var detected_pointertypes = {};
+
+ async function run() {
+ var target0 = document.getElementById("target0");
+ var innerframe = document.getElementById("innerframe");
+ var target1 = innerframe.contentWindow.document.getElementsByTagName("div")[0];
+ var pointerover_pointerId = null;
+ var pointerover_pointerType = null;
+
+ var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
+ var receivedEvents = {};
+ var receivedEventsInnerFrame = {};
+
+
+ function checkPointerId(event, inner) {
+ detected_pointertypes[event.pointerType] = true;
+ var eventName = (inner ? "inner frame " : "" ) + event.type;
+ test_pointerEvent.step(function() {
+ assert_equals(event.pointerId, pointerover_pointerId, "PointerId of " + eventName + " is not correct");
+ assert_equals(event.pointerType, pointerover_pointerType, "PointerType of " + eventName + " is not correct");
+ }, eventName + ".pointerId were the same as first pointerover");
+ }
+
+ on_event(window, "message", function(event) {
+ if (event.source != innerframe.contentWindow) {
+ return;
+ }
+ receivedEventsInnerFrame[event.data.type] = 1;
+ checkPointerId(event.data, true);
+ if (Object.keys(receivedEvents).length == eventList.length && Object.keys(receivedEventsInnerFrame).length == eventList.length)
+ test_pointerEvent.done();
+ });
+
+ eventList.forEach(function(eventName) {
+ on_event(target0, eventName, function (event) {
+ if (pointerover_pointerId === null && event.type == 'pointerover') {
+ pointerover_pointerId = event.pointerId;
+ pointerover_pointerType = event.pointerType;
+ } else {
+ checkPointerId(event, false);
+ }
+ receivedEvents[event.type] = 1;
+ });
+ });
+
+ var iframeRect = innerframe.getClientRects()[0];
+ var rect = target1.getClientRects()[0];
+ var center_x = Math.round(iframeRect.left + (rect.left + rect.right) / 2);
+ var center_y = Math.round(iframeRect.top + (rect.top + rect.bottom) / 2);
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(center_x, center_y)
+ .pointerMove(center_y, center_y + 100)
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events pointerdown tests</h1>
+ Complete the following actions:
+ <ol>
+ <li>Start with your pointing device outside of black box, then move it into black box. If using touch just press in black box and don't release.
+ <li>Move your pointing device into purple box (without leaving the digitizer range if you are using hover supported pen or without releasing touch if using touch). Then move it out of the purple box.
+ </ol>
+ <div id="target0" class="touchActionNone">
+ </div>
+ <iframe src="resources/pointerevent_pointerId_scope-iframe.html" id="innerframe"></iframe>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html b/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html
new file mode 100644
index 0000000000..bf2a405f72
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Redundant "pointerenter" shouldn't be fired without "pointerleave"s</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>
+"use strict";
+
+function stringifyEvents(eventArray) {
+ if (!eventArray.length) {
+ return "[]";
+ }
+ let result = "";
+ eventArray.forEach(event => {
+ if (result != "") {
+ result += ", ";
+ }
+ result += `${event.type}@${
+ event.target?.nodeType == Node.ELEMENT_NODE
+ ? `${event.target.localName}${
+ event.target.id ? `#${event.target.id}` : ""
+ }`
+ : event.target?.localName
+ }`;
+ });
+ return result;
+}
+
+function eventsAfterClick(eventArray) {
+ const indexAtClick = eventArray.findIndex(e => e.type == "click");
+ if (indexAtClick >= 0) {
+ return eventArray.slice(indexAtClick + 1);
+ }
+ return [];
+}
+
+addEventListener("load", () => {
+ promise_test(async () => {
+ const div1 = document.createElement("div");
+ div1.setAttribute("id", "grandparent");
+ div1.setAttribute("style", "width: 32px; height: 32px");
+ const div2 = document.createElement("div");
+ div2.setAttribute("id", "parent");
+ div2.setAttribute("style", "width: 32px; height: 32px");
+ const div3 = document.createElement("div");
+ div3.setAttribute("id", "child");
+ div3.setAttribute("style", "width: 32px; height: 32px");
+ div1.appendChild(div2);
+ div2.appendChild(div3);
+ document.body.appendChild(div1);
+ const bodyRect = document.body.getBoundingClientRect();
+ const div3Rect = div3.getBoundingClientRect();
+ let events = [];
+ for (const type of ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointermove"]) {
+ for (const node of [document.body, div1, div2, div3]) {
+ node.addEventListener(type, event => {
+ if (event.target == node) {
+ events.push({type: event.type, target: event.target});
+ }
+ }, {capture: true});
+ }
+ }
+ div3.addEventListener("click", event => {
+ div3.remove();
+ events.push({type: event.type, target: event.target});
+ }, {once: true});
+ await new test_driver.Actions()
+ .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {})
+ .pointerDown()
+ .pointerUp() // The clicked in the child, then it's removed from the DOM tree
+ .pointerMove(bodyRect.x + 10, bodyRect.y + 10, {}) // Then, move onto the <body>
+ .send();
+ // FYI: Comparing `pointerenter`s before `click` requires additional
+ // initialization, but it's out of scope of this bug. Therefore, we
+ // compare only events after `click`.
+ const expectedEvents = [ // no events should be fired on the child due to disconnected
+ { type: "pointerleave", target: div2}, // no pointerout because of first pointer move after the mutation
+ { type: "pointerleave", target: div1},
+ { type: "pointerover", target: document.body},
+ { type: "pointermove", target: document.body},
+ ];
+ assert_equals(
+ stringifyEvents(eventsAfterClick(events)),
+ stringifyEvents(expectedEvents),
+ );
+ div1.remove();
+ }, "After removing the last over element, redundant pointerenter events should not be fired on the ancestors");
+
+ promise_test(async () => {
+ const hostContainer = document.createElement("div");
+ hostContainer.setAttribute("id", "containerOfShadowHost");
+ hostContainer.setAttribute("style", "margin-top: 32px; height: 32px");
+ const host = document.createElement("div");
+ host.setAttribute("id", "shadowHost");
+ host.setAttribute("style", "width: 32px; height: 32px");
+ const root = host.attachShadow({mode: "open"});
+ const rootElementInShadow = document.createElement("div");
+ root.appendChild(rootElementInShadow);
+ rootElementInShadow.setAttribute("id", "divInShadow");
+ rootElementInShadow.setAttribute("style", "width: 32px; height: 32px");
+ hostContainer.appendChild(host);
+ document.body.appendChild(hostContainer);
+ const bodyRect = document.body.getBoundingClientRect();
+ const rootElementInShadowRect = rootElementInShadow.getBoundingClientRect();
+ let events = [];
+ for (const type of ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointermove"]) {
+ for (const node of [document.body, hostContainer, host, root, rootElementInShadow]) {
+ node.addEventListener(type, event => {
+ if (event.target == node) {
+ events.push({type: event.type, target: event.target});
+ }
+ }, {capture: true});
+ }
+ }
+ rootElementInShadow.addEventListener("click", event => {
+ rootElementInShadow.remove();
+ events.push({type: event.type, target: event.target});
+ }, {once: true});
+ await new test_driver.Actions()
+ .pointerMove(rootElementInShadowRect.x + 10, rootElementInShadowRect.y + 10, {})
+ .pointerDown()
+ .pointerUp() // The clicked root element in the shadow is removed here.
+ .pointerMove(bodyRect.x + 10, bodyRect.y + 10, {}) // Then, move onto the <body>
+ .send();
+ // FYI: Comparing `pointerenter`s before `click` requires additional
+ // initialization, but it's out of scope of this bug. Therefore, we
+ // compare only events after `click`.
+ const expectedEvents = [ // no events should be fired on rootElementInShadow due to disconnected
+ { type: "pointerleave", target: host}, // no pointerout because of first pointer move after the mutation
+ { type: "pointerleave", target: hostContainer},
+ { type: "pointerover", target: document.body},
+ { type: "pointermove", target: document.body},
+ ];
+ assert_equals(
+ stringifyEvents(eventsAfterClick(events)),
+ stringifyEvents(expectedEvents),
+ );
+ hostContainer.remove();
+ }, "After removing the root element in the shadow under the cursor, pointerleave events should be targeted outside the shadow, but redundant pointerenter events should not be fired");
+}, {once: true});
+</script>
+</head>
+<body style="padding-top: 32px"></body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointercancel_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_pointercancel_touch.html
new file mode 100644
index 0000000000..687b6a3f32
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointercancel_touch.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerCancel - touch</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>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h1>pointercancel test</h1>
+ <h3>Warning: this test works properly only for devices that have touchscreen</h3>
+ <h4>
+ Test Description: This test checks if pointercancel event triggers.
+ <p>Start touch over the black rectangle and then move your finger to scroll the page.</p>
+ </h4>
+ <p>
+ <div id="target0" style="background: black"></div>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointercancel event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointerdown_event = null;
+ var pointercancel_event = null;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ on_event(target0, "pointerdown", function (event) {
+ pointerdown_event = event;
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, "pointercancel", function (event) {
+ pointercancel_event = event;
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointerdown_event, null, "pointerdown was received: ");
+ const properties = [
+ "pointerId", "width", "height",
+ "pressure", "tangentialPressure", "tiltX", "tiltY",
+ "twist", "altitudeAngle", "azimuthAngle",
+ "pointerType", "isPrimary"
+ ];
+ for (let property in properties) {
+ assert_equals(event[property],
+ pointerdown_event[property],
+ property + " should be the same for pointerdown and pointercancel");
+ }
+ });
+ test_pointerEvent.step(function () {
+ check_PointerEvent(event);
+ });
+ test_pointerEvent.step(function () {
+ assert_equals(event.x, 0, "pointercancel.x must be zero");
+ assert_equals(event.y, 0, "pointercancel.x must be zero");
+ assert_equals(event.clientX, 0, "pointercancel.clientX must be zero");
+ assert_equals(event.clientY, 0, "pointercancel.clientY must be zero");
+ });
+ });
+
+ on_event(target0, "pointerout", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointercancel_event, null, "pointercancel was received before pointerout: ");
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerId should be the same for pointerout and pointercancel");
+ assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType should be the same for pointerout and pointercancel");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "isPrimary should be the same for pointerout and pointercancel");
+ });
+ });
+
+ on_event(target0, "pointerleave", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointercancel_event, null, "pointercancel was received before pointerleave: ");
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerId should be the same for pointerleave and pointercancel");
+ assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType should be the same for pointerleave and pointercancel");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "isPrimary should be the same for pointerleave and pointercancel");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down');
+ }
+ </script>
+ <h1>Pointer Events pointercancel Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-custom-element.html b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-custom-element.html
new file mode 100644
index 0000000000..6f7c9058d6
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-custom-element.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>PointerCapture for Custome Shadow DOM</title>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width">
+ <link rel="help" href= "https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+ <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>
+ class WC extends HTMLElement{
+ constructor(){
+ super();
+ let template = document.getElementById('template-wc');
+ let node = template.content.cloneNode(true) ;
+
+ let shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.appendChild(node);
+ }
+ }
+ customElements.define("wc-wc", WC);
+ </script>
+ </head>
+ <body onload="onLoad()">
+ <template id="template-wc">
+ <style>
+ #content{
+ height:50px;
+ width:50px;
+ background-color: magenta;
+ }
+ </style>
+ <div id="content"></div>
+ </template>
+ <h4>PointerCapture by Custom Element's Shadow DOM</h4>
+ The magenta box below is part of a custom element's Shadow DOM.
+ <ul>
+ <li> Click left mouse button inside the box and keep mouse button depressed</li>
+ <li> Move the mouse</li>
+ <li> There should be a message stating <em>Pointer was captured by custom element's Shadow DOM!</em></li>
+ <li> Release left mouse button</li>
+ <li> There should be a message stating <em>Pointer was released by custom element's Shadow DOM!</em></li>
+ </ul>
+
+ <wc-wc id="wc-wc"></wc-wc>
+ <div id="log"></div>
+ <script>
+ function onLoad(){
+ var logDiv = document.getElementById("log");
+ function logMessage(message){
+ var log = document.createElement("div");
+ var messageNode = document.createTextNode(message);
+ log.appendChild(messageNode);
+ logDiv.appendChild(log);
+ }
+ var events = [];
+
+ var content = document.getElementById("wc-wc")
+ .shadowRoot.getElementById("content");
+
+ content.addEventListener("pointerdown", function(e){
+ content.setPointerCapture(e.pointerId);
+ events.push("pointerdown@content");
+ });
+ content.addEventListener("gotpointercapture", function(e){
+ logMessage("Pointer was captured by custom element's Shadow DOM!");
+ events.push("gotpointercapture@content");
+ });
+ content.addEventListener("pointerup", function(e){
+ content.releasePointerCapture(e.pointerId);
+ events.push("pointerup@content");
+ });
+ content.addEventListener("lostpointercapture", function(e){
+ logMessage("Pointer was released by custom element's Shadow DOM!");
+ events.push("lostpointercapture@content");
+ if(window.promise_test && wc_shadow_dom_test){
+ wc_shadow_dom_test.step(function(){
+ assert_array_equals(events, ["pointerdown@content",
+ "gotpointercapture@content", "pointerup@content",
+ "lostpointercapture@content"]);
+ resolve_test();
+ });
+ }
+ });
+
+ var wc_shadow_dom_test = null;
+ var resolve_test = null;
+ var reject_test = null;
+
+ function cleanup(){
+ events = [];
+ shadow_dom_test = null;
+ resolve_test = null;
+ reject_test = null;
+ }
+ if(window.promise_test){
+ promise_test(async function(t){
+ var actions_promise;
+ return new Promise(async function(resolve, reject){
+ wc_shadow_dom_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ t.add_cleanup(function(){
+ cleanup();
+ });
+ var contentRect = content.getBoundingClientRect();
+ var actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(Math.ceil(contentRect.x), Math.ceil(contentRect.y))
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }).then(async()=>{
+ await actions_promise;
+ t.done();
+ });
+ }, "PointerCapture works for custom element Shadow DOM.");
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-shadow-dom.html b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-shadow-dom.html
new file mode 100644
index 0000000000..94636231a6
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-in-shadow-dom.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>PointerCapture for Shadow DOM Elements</title>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width">
+ <link rel="help" href= "https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+ <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>
+ <body onload="onLoad()">
+ <template id="template">
+ <style>
+ #content{
+ height:100px;
+ width:100px;
+ background-color: lightgrey;
+ }
+ </style>
+ <div id="content"></div>
+ </template>
+ <h4>PointerCapture by Shadow DOM element</h4>
+ The light gray box below is part of Shadow DOM.
+ <ul>
+ <li> Click left mouse button inside the box and keep mouse button depressed </li>
+ <li> Move the mouse </li>
+ <li> There should be a message stating <em>Pointer was captured by Shadow DOM!</em></li>
+ <li> Release left mouse button
+ <li> There should be a message stating <em>Pointer was released by Shadow DOM!</em></li>
+ </ul>
+ <div id="shadowhost"></div>
+ <div id="log"></div>
+ <script>
+ function onLoad(){
+ var logDiv = document.getElementById("log");
+ function logMessage(message){
+ var log = document.createElement("div");
+ var messageNode = document.createTextNode(message);
+ log.appendChild(messageNode);
+ logDiv.appendChild(log);
+ }
+ var events = [];
+
+ var host = document.getElementById("shadowhost");
+ var shadowRoot = host.attachShadow({mode: "open"});
+ var template = document.getElementById("template");
+ var node = template.content.cloneNode(true);
+ shadowRoot.appendChild(node);
+
+ var content = host.shadowRoot.getElementById("content");
+
+ content.addEventListener("pointerdown", function(e){
+ content.setPointerCapture(e.pointerId);
+ events.push("pointerdown@content");
+ });
+ content.addEventListener("gotpointercapture", function(e){
+ logMessage("Pointer was captured by Shadow DOM!");
+ events.push("gotpointercapture@content");
+ });
+ content.addEventListener("pointerup", function(e){
+ content.releasePointerCapture(e.pointerId);
+ events.push("pointerup@content");
+ });
+ content.addEventListener("lostpointercapture", function(e){
+ logMessage("Pointer was released by Shadow DOM!");
+ events.push("lostpointercapture@content");
+ if(window.promise_test && shadow_dom_test){
+ shadow_dom_test.step(function(){
+ assert_array_equals(events, ["pointerdown@content",
+ "gotpointercapture@content", "pointerup@content",
+ "lostpointercapture@content"]);
+ resolve_test();
+ });
+ }
+ });
+
+ var shadow_dom_test = null;
+ var resolve_test = null;
+ var reject_test = null;
+
+ function cleanup(){
+ events = [];
+ shadow_dom_test = null;
+ resolve_test = null;
+ reject_test = null;
+ }
+
+ if(window.promise_test){
+ promise_test(async function(t){
+ var actions_promise;
+ return new Promise(async function(resolve, reject){
+ shadow_dom_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ t.add_cleanup(function(){
+ cleanup();
+ });
+ var contentRect = content.getBoundingClientRect();
+ var actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(Math.ceil(contentRect.x), Math.ceil(contentRect.y))
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }).then(async ()=>{
+ await actions_promise;
+ t.done();
+ });
+ }, "PointerCapture works for Shadow DOM element.");
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-not-lost-in-chorded-buttons.html b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-not-lost-in-chorded-buttons.html
new file mode 100644
index 0000000000..4f044f5533
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture-not-lost-in-chorded-buttons.html
@@ -0,0 +1,225 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Set/Release capture when using chorded buttons</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1053385">
+ <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>
+ .container {
+ height: 500px;
+ width: 500px;
+ border: 1px solid black;
+ overflow: hidden;
+ position: relative;
+ }
+
+ #box {
+ height: 50px;
+ width: 50px;
+ background: red;
+ position: absolute;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Pointer Events Capture Test - capture should not be lost early</h1>
+ <h4>
+ Test Description: This test checks if setCapture/pointerup functions
+ works properly. Complete the following actions:
+ <ol>
+ <li> Put your mouse over the red box
+ <li> Press and hold left mouse button. Box will call setPointerCapture
+ <li> Press right button and release
+ <li> Pointer capture should not be lost
+ <li> Press right button again and release
+ <li> Pointer capture should not be lost
+ <li> Release left mouse button. lostpointercapture is called
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div class="container">
+ <div id="box"></div>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ var PhaseEnum = {
+ WaitingForDown: "down",
+ WaitingForUp: "up",
+ UpDone : "up_done"
+ };
+
+ var origin = {x:0, y:0};
+ var position = {x:0, y:0};
+ var deltaX = 0;
+ var deltaY = 0;
+ var box = document.getElementById("box");
+ var logDiv = document.getElementById("log");
+
+ var currentPhase = PhaseEnum.WaitingForDown;
+ var events = [];
+
+ function slide(event){
+ // move the target following the mouse
+ deltaX = event.clientX - origin.x
+ deltaY = event.clientY - origin.y
+ box.style.left = `${position.x + deltaX}px`;
+ box.style.top = `${position.y + deltaY}px`;
+ }
+
+ function addLog(message){
+ var messageDiv = document.createElement("div");
+ var textContent = document.createTextNode(message);
+ messageDiv.appendChild(textContent);
+ logDiv.appendChild(messageDiv);
+ }
+
+ function handle_pointerdown(e){
+ box.setPointerCapture(e.pointerId);
+ if(window.promise_test){
+ current_test.step(function(){
+ // once receiving a pointer down and the pointer is captured,
+ // no other mousedown should send pointerdown events during the test
+ assert_equals(currentPhase, PhaseEnum.WaitingForDown,
+ "Current Phase should be " + PhaseEnum.WaitingForDown);
+ currentPhase = PhaseEnum.WaitingForUp;
+ events.push("target@pointerdown");
+ });
+ }
+ origin = { x: event.clientX, y: event.clientY };
+ box.addEventListener("pointermove", slide);
+ }
+
+ function handle_pointerup(e){
+ box.releasePointerCapture(e.pointerId);
+ if(window.promise_test){
+ current_test.step(function(){
+ assert_equals(event.buttons, 0,
+ 'pointerup should happen when all buttons are released.');
+ assert_equals(currentPhase, PhaseEnum.WaitingForUp,
+ "Current Phase should be " + PhaseEnum.WaitingForUp);
+ currentPhase = PhaseEnum.UpDone;
+ events.push("target@pointerup");
+ });
+ }
+ box.removeEventListener("pointermove", slide);
+ }
+
+ function handle_contextmenu(e){
+ e.preventDefault();
+ }
+
+ function handle_lostpointercapture(e){
+ if(window.promise_test){
+ current_test.step(function(){
+ events.push("target@lostpointercapture");
+ assert_equals(currentPhase, PhaseEnum.UpDone,
+ "Current Phase should be " + PhaseEnum.UpDone + "." +
+ 'lostpointercapture should happen after pointerup event.');
+ assert_equals(event.buttons, 0,
+ 'lostpointercapture should happen when all buttons are released.');
+ assert_array_equals(events, ["target@pointerdown",
+ "target@pointerup", "target@lostpointercapture"]);
+ resolve_test();
+ current_test.done();
+ });
+ }
+ if(event.buttons === 0){
+ addLog("Test Passed!");
+ }else{
+ addLog("Test Failed!");
+ }
+ }
+
+ function removeEventListeners(){
+ box.removeEventListener('pointerdown', handle_pointerdown);
+ box.removeEventListener('pointerup', handle_pointerup);
+ box.removeEventListener('contextmenu', handle_contextmenu);
+ box.removeEventListener('lostpointercapture',
+ handle_lostpointercapture);
+ }
+
+ function addEventListeners(){
+ box.addEventListener('pointerdown', handle_pointerdown);
+ box.addEventListener('pointerup', handle_pointerup);
+ box.addEventListener('contextmenu', handle_contextmenu);
+ box.addEventListener('lostpointercapture',
+ handle_lostpointercapture);
+ }
+
+ var current_test = null;
+ var resolve_test = null;
+ var reject_test = null;
+ // window.promise_test is only defined when running the
+ // test using testharness.js
+ // if window.promise_test is not defined we'll run the manual testing
+ // path
+ if(!window.promise_test){
+ addEventListeners();
+ }
+
+ if(window.promise_test){
+ promise_test(function(t){
+ addEventListeners();
+ t.add_cleanup(function(){
+ removeEventListeners();
+ currentPhase = PhaseEnum.WaitingForDown;
+ events = [];
+ });
+ return new Promise(function(resolve, reject){
+ current_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ var actions = new test_driver.Actions();
+ var actions_promise = actions
+ .pointerMove(0, 0, {origin: box})
+
+ .pointerDown({button: actions.ButtonType.LEFT})
+ // Ensure clicking other buttons while a first button has
+ // captured the pointer doesn't release the capture
+ .pointerDown({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.RIGHT})
+ .pointerDown({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ })
+ }, "Pointer Events Capture Test - capture not lost due to " +
+ "chorded buttons interaction");
+
+ promise_test(function(t){
+ addEventListeners();
+ t.add_cleanup(function(){
+ removeEventListeners();
+ currentPhase = PhaseEnum.WaitingForDown;
+ events = [];
+ });
+ return new Promise(function(resolve, reject){
+ current_test = t;
+ resolve_test = resolve;
+ reject_test = reject;
+ var actions = new test_driver.Actions();
+ var actions_promise = actions
+ .pointerMove(0, 0, {origin: box})
+
+ .pointerDown({button: actions.ButtonType.LEFT})
+ // Ensure clicking other buttons while a first button has
+ // captured the pointer doesn't release the capture
+ .pointerDown({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ })
+ }, "Pointer Events Capture Test - capture not lost " +
+ "due to combination of left and right chorded buttons interaction.");
+ }
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointercapture_in_frame.html b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture_in_frame.html
new file mode 100644
index 0000000000..14be00968f
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointercapture_in_frame.html
@@ -0,0 +1,252 @@
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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 type="text/javascript" src="pointerevent_support.js"></script>
+<style>
+iframe {
+ width: 300px;
+ height: 300px;
+ top: 100px;
+ left: 100px;
+ border: 0;
+ position: absolute;
+ background: green;
+}
+#outerFrame {
+ width: 500px;
+ height: 500px;
+ background: blue;
+}
+body {
+ touch-action:none;
+}
+</style>
+<body id="outerFrame body" onload="run()">
+<div id='outerFrame'>
+<iframe id='innerFrameElement' src="resources/pointerevent_pointercapture-iframe.html"></iframe>
+</div>
+</body>
+<script>
+var receivedEventList = [];
+// |start_logging| is set to true in the pointerdown event handler, and as such
+// also tracks if pointerdown happened already in the test.
+var start_logging = false;
+var first_pointermove_happened = false;
+
+// Add a timeout promise when waiting for lostpointercapture and pointerup.
+// We add this to make it easy to know which event wasn't fired on the right
+// element.
+var eventTimeout = ()=>(()=>new Promise((resolve, reject)=>{
+ const msToWait = 4000;
+ let start;
+ function wait(timestamp){
+ if(start === undefined)
+ start = timestamp;
+ if(timestamp - start < msToWait)
+ requestAnimationFrame(wait);
+ else
+ resolve();
+ }
+ requestAnimationFrame(wait);
+}));
+
+
+function handleEvent(event) {
+ if (event.type == 'pointerdown') {
+ start_logging = true;
+ if (document.setPointerCaptureOnPointerDown) {
+ event.target.setPointerCapture(event.pointerId);
+ }
+ }
+
+ // Only log the first pointermove event after pointerdown. We need to account
+ // for coalesced pointermove events and for the pointermove events that
+ // happen after lostpointercapture.
+ if (start_logging && (event.type !== "pointermove" || !first_pointermove_happened))
+ receivedEventList.push(event.target.id + ' received ' + event.type);
+
+ if (event.type == "pointermove"){
+ if (document.releasePointerCaptureOnFirstMove && event.target.hasPointerCapture(event.pointerId))
+ event.target.releasePointerCapture(event.pointerId);
+ if(start_logging)
+ first_pointermove_happened = true;
+ }
+};
+
+document.testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture'];
+document.testEventList.forEach(function(eventName) {
+ document.getElementById('outerFrame').addEventListener(eventName, handleEvent);
+});
+
+function Reset() {
+ document.setPointerCaptureOnPointerDown = false;
+ document.releasePointerCaptureOnFirstMove = false;
+ receivedEventList = [];
+ start_logging = false;
+ first_pointermove_happened = false;
+}
+
+function run() {
+ var pointerType = location.search.substring(1);
+ promise_test (async(t) => {
+ Reset();
+ document.setPointerCaptureOnPointerDown = true;
+ expectedEventList = ["innerFrame received pointerdown",
+ "innerFrame received gotpointercapture",
+ "innerFrame received pointermove",
+ "innerFrame received pointerup",
+ "innerFrame received lostpointercapture"];
+ var pointerId = pointerType + "Pointer1";
+
+ var innerFrame = document.getElementById('innerFrameElement');
+ var innerFrameDocument = innerFrame.contentDocument;
+ // We are interested in tracking events only after pointerdown
+ var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);});
+ var watcher_promise = pointerdown_happened.then(()=>{
+ var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["lostpointercapture"], eventTimeout());
+ return watch_inner_frame.wait_for(["lostpointercapture"]);
+ });
+
+ await new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(200, 200)
+ .pointerDown()
+ .pointerMove(150, 150)
+ .pointerMove(50, 50)
+ .pointerUp()
+ .pointerMove(75, 75)
+ .send();
+ // Wait for lostpointercapture to fire.
+ await watcher_promise;
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ document.setPointerCaptureOnPointerDown = false;
+ }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at inner frame and set pointer capture.");
+
+ promise_test (async(t) => {
+ Reset();
+ document.setPointerCaptureOnPointerDown = true;
+ expectedEventList = ["outerFrame received pointerdown",
+ "outerFrame received gotpointercapture",
+ "outerFrame received pointermove",
+ "outerFrame received pointerup",
+ "outerFrame received lostpointercapture"];
+ var pointerId = pointerType + "Pointer1";
+
+ // We are interested in tracking events only after pointerdown
+ var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);});
+ var watcher_promise = pointerdown_happened.then(()=>{
+ // For this test we're going to wait for both pointerup and lostpointercapture,
+ // as pointerup fires on innerFrame on Windows (see crbug.com/1186788)
+ var watch_outer_frame_for_pointerup_and_lostpointercapture =
+ new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup", "lostpointercapture"], eventTimeout());
+ return watch_outer_frame_for_pointerup_and_lostpointercapture.wait_for(["pointerup", "lostpointercapture"]);
+ });
+
+ await new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(25, 25)
+ .pointerDown()
+ .pointerMove(200, 200)
+ .pointerUp()
+ .pointerMove(25, 25)
+ .send();
+ // Wait for lostpointercapture to fire.
+ await watcher_promise;
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ document.setPointerCaptureOnPointerDown = false;
+ }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at outer frame body and set pointer capture.");
+
+
+ promise_test (async(t) => {
+ Reset();
+ document.setPointerCaptureOnPointerDown = true;
+ document.releasePointerCaptureOnFirstMove = true;
+ // Mouse event has the frame capture, so after pointer capture released, events are
+ // dispatched to innerFrameDocument.
+ expectedEventList = ["innerFrame received pointerdown",
+ "innerFrame received gotpointercapture",
+ "innerFrame received pointermove",
+ "innerFrame received lostpointercapture",
+ (pointerType == "touch" ? "outerFrame": "innerFrameDocument") + " received pointerup",];
+ var pointerId = pointerType + "Pointer1";
+
+ var innerFrame = document.getElementById('innerFrameElement');
+ var innerFrameDocument = innerFrame.contentDocument;
+ // We are interested in tracking events only after pointerdown
+ var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);});
+ var watcher_promise = pointerdown_happened.then(()=>{
+ if(pointerType === "touch"){
+ var watch_outer_frame = new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup"], eventTimeout());
+ return watch_outer_frame.wait_for("pointerup");
+ }else{
+ var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout());
+ return watch_inner_frame.wait_for("pointerup");
+ }
+ });
+
+ await new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(200, 200)
+ .pointerDown()
+ .pointerMove(150, 150)
+ .pointerMove(50, 50)
+ .pointerUp()
+ .pointerMove(150, 150)
+ .send();
+ // Wait for pointerup to fire.
+ await watcher_promise;
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ document.releasePointerCaptureOnFirstMove = false;
+ document.setPointerCaptureOnPointerDown = false;
+ }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at inner frame, then release on next pointermove.");
+
+
+ promise_test (async(t) => {
+ Reset();
+ document.setPointerCaptureOnPointerDown = true;
+ document.releasePointerCaptureOnFirstMove = true;
+ expectedEventList = ["outerFrame received pointerdown",
+ "outerFrame received gotpointercapture",
+ "outerFrame received pointermove",
+ "outerFrame received lostpointercapture",
+ "innerFrame received pointerup"];
+ var pointerId = pointerType + "Pointer1";
+
+ var innerFrame = document.getElementById('innerFrameElement');
+ var innerFrameDocument = innerFrame.contentDocument;
+ // We are interested in tracking events only after pointerdown
+ var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);});
+ var watcher_promise = pointerdown_happened.then(()=>{
+ var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout());
+ return watch_inner_frame.wait_for(["pointerup"]);
+ });
+
+ await new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(50, 50)
+ .pointerDown()
+ .pointerMove(200, 200)
+ // Pause here to make sure that the previous and following pointer moves
+ // are not coalesced. If they are coalesced, we will not see the second
+ // move event which is when the pending lostpointercapture should be fired
+ // (Since the pointerup event is targeted at a different frame, it won't dispatch
+ // the pending lostpointercapture event).
+ .pause(300)
+ .pointerMove(250, 250)
+ .pointerUp()
+ .send();
+ // Wait for pointerup to fire.
+ await watcher_promise;
+ assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+ document.releasePointerCaptureOnFirstMove = false;
+ document.setPointerCaptureOnPointerDown = false;
+ }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at outer frame, then release on next pointermove.");
+}
+</script>
+
+</body>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerenter_does_not_bubble.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerenter_does_not_bubble.html
new file mode 100644
index 0000000000..5193e7ab0b
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerenter_does_not_bubble.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: The pointerenter event does not bubble </title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="The pointerenter event must not bubble up to parent elements."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <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>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerEnter event does not bubble"); // set up test harness
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointerenter_event = null;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var parent0 = document.getElementById("parent0");
+ var actions_promise;
+
+ on_event(target0, "pointerenter", function (event) {
+ pointerenter_event = event;
+
+ test_pointerEvent.step(function () {
+ assert_equals(event.type, "pointerenter", "pointer event received: " + event.type);
+ assert_false(event.bubbles, "pointerenter event.bubbles should be false: " + event.bubbles);
+ });
+ });
+ on_event(target0, "pointerout", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointerenter_event, null, "pointerout event was never received: ");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // parent
+ on_event(parent0, "pointerenter", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointerEvent.step(function () {
+ assert_equals(event.target.id, "parent0", "Recieved " + event.type + " in parent for " + event.target.id);
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(-10, -10, {origin: target0})
+ .pointerMove(-20, -20, {origin: target0})
+ .pointerMove(0, 0)
+ .send();
+ }
+
+ </script>
+ <style>
+ #target0 {
+ background: purple;
+ border: 1px solid orange;
+ width:50px;
+ height:50px;
+ }
+ #parent0 {
+ background: black;
+ border: 1px solid orange;
+ width:100px;
+ height:100px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1> Pointer Event: pointerenter does not bubble</h1>
+ <h4>
+ Test Description:
+ The pointerenter event must not bubble up to parent elements.
+ </h4>
+ <div id="instructions">
+ Use the mouse or pen to hover over then out of the purple box nested in the black box. Or with touch, tap on the purple box.
+ </div>
+ <div id="parent0">
+ <div id="target0"></div>
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html
new file mode 100644
index 0000000000..8425667a10
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerleave after pointercancel</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h2>pointerleave after pointercancel</h2>
+ <h4>Test Description: This test checks if pointerleave event triggers after pointercancel. Start touch on the black rectangle and move your touch to scroll in any direction. </h4>
+ <p>Note: this test is for touch devices only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerleave = async_test("pointerleave event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointercancel_event = null;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ on_event(target0, "pointercancel", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ pointercancel_event = event;
+ });
+
+ // After firing the pointercancel event the pointerleave event must be dispatched.
+ // TA: 4.3.1
+ on_event(target0, "pointerleave", function (event) {
+ if(event.pointerType == 'touch') {
+ if(pointercancel_event != null) {
+ test_pointerleave.step(function() {
+ assert_equals(event.pointerType, pointercancel_event.pointerType, "pointerType is same for pointercancel and pointerleave");
+ assert_equals(event.isPrimary, pointercancel_event.isPrimary, "isPrimary is same for pointercancel and pointerleave");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerleave.done();
+ });
+ }
+ else {
+ test_pointerleave.step(function() {
+ assert_unreached("pointerleave received before pointercancel");
+ }, "pointerleave received before pointercancel");
+ }
+ }
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down');
+ }
+
+ </script>
+ <h1>Pointer Events pointerleave tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendant_over.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendant_over.html
new file mode 100644
index 0000000000..ae348307b4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendant_over.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerleave + descendant</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>pointerleave</h1>
+ <h4>
+ Test Description: This test checks if pointerleave event works properly.
+ <ol>
+ <li>Put your mouse over the black rectangle
+ <li>Then move it into the purple rectangle
+ <li>Click on the purple rectangle to complete the test
+ </ol>
+ Note: when you entered the black rectangle once don't leave it before the end of the test to get proper results.
+ </h4>
+ <p>
+ <div id="target0" style="background:black">
+ <div id="target1" style="background:purple"></div>
+ </div>
+ <script>
+ var eventTested = false;
+ var pointerleaveReceived = false;
+ var detected_pointertypes = {};
+ var test_pointerleave = async_test("pointerleave shouldn't be received on descendant's pointerover");
+ var actions_promise;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ // The pointerleave event must not be dispatched when the pointer enters a child element without leaving the hit test boundaries of the parent. (as distinct from pointerout)
+ // TA: 9.2
+ on_event(target1, "pointerdown", function(event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ test_pointerleave.step(function() {
+ assert_true(!pointerleaveReceived, "pointerleave shouldn't be received on descendant's pointerover");
+ }, "pointerleave shouldn't be received on descendant's pointerover");
+ actions_promise.then( () => {
+ test_pointerleave.done();
+ });
+ });
+
+ on_event(target0, "pointerleave", function (event) {
+ if (eventTested == false) {
+ pointerleaveReceived = true;
+ eventTested = true;
+ }
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(0, 0, {origin: target1})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events pointerleave tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendants.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendants.html
new file mode 100644
index 0000000000..6133b05c64
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_descendants.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointerleave + descendant</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>pointerleave</h1>
+ <h4>
+ Test Description: This test checks if pointerleave event works properly.
+ <p>Put your mouse over the black rectangle and then move it out through purple rectangle boundaries.</p>
+ </h4>
+ <p>
+ <div id="target0" style="background:black; height: 47px;">
+ <div style="background:purple; height: 35px; width: 90%; position: absolute"></div>
+ </div>
+ <script>
+ var detected_pointertypes = {};
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.querySelector("#target0 > div");
+ var test_pointerleave = async_test("pointerleave event received");
+ var actions_promise;
+
+ on_event(target0, "pointerover", function(event) {
+ detected_pointertypes[ event.pointerType ] = true;
+ });
+
+ // When a pointing device is moved off of the hit test boundaries of an element and all of its descendants, the pointerleave event must be dispatched.
+ // TA: 9.1
+ on_event(target0, "pointerleave", function (event) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerleave.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(0, 0, {origin: target1})
+ .pointerMove(0, 0)
+ .send();
+ }
+ </script>
+ <h1>Pointer Events pointerleave tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_does_not_bubble.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_does_not_bubble.html
new file mode 100644
index 0000000000..50af44cfba
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_does_not_bubble.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: The pointerleave event does not bubble </title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="The pointerleave event must not bubble up to parent elements."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <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>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerLeave event does not bubble"); // set up test harness
+ var actions_promise;
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var parent0 = document.getElementById("parent0");
+
+ on_event(target0, "pointerleave", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ test_pointerEvent.step(function () {
+ assert_equals(event.type, "pointerleave", "pointer event received: " + event.type);
+ assert_false(event.bubbles, "pointerleave event.bubbles should be false: " + event.bubbles);
+ });
+ });
+
+ on_event(parent0, "pointerleave", function (event) {
+ test_pointerEvent.step(function () {
+ assert_equals(event.target.id, "parent0", "Recieved " + event.type + " in parent for " + event.target.id);
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(0, 0)
+ .send();
+ }
+ </script>
+ <style>
+ #target0 {
+ background: purple;
+ border: 1px solid orange;
+ width:50px;
+ height:50px;
+ }
+ #parent0 {
+ background: black;
+ border: 1px solid orange;
+ width:100px;
+ height:100px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1> Pointer Event: pointerleave does not bubble</h1>
+ <h4>
+ Test Description:
+ The pointerleave event must not bubble up to parent elements.
+ </h4>
+ <div id="instructions">
+ Use the mouse or pen to hover over then out of the purple box nested in the black box. Or with touch, tap on the purple box.
+ </div>
+ <div id="parent0">
+ <div id="target0"></div>
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_pen-manual.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_pen-manual.html
new file mode 100644
index 0000000000..d0d8dd3682
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerleave_pen-manual.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Dispatch pointerleave (pen). </title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="When a pointing device that supports hover (pen stylus) leaves the range of the digitizer while over an element, the pointerleave event must be dispatched."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerleave event"); // set up test harness
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var count = 0;
+
+ on_event(target0, "pointerenter", function (event) {
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerType, "pen", "Test should be run using a pen as input");
+ assert_equals(event.type, "pointerenter", "The " + event.type + " event was received");
+ assert_equals(event.isPrimary, true, "The " + event.type + ".isPrimary is true");
+ });
+ });
+
+ on_event(target0, "pointerleave", function (event) {
+ count++;
+ detected_pointertypes[event.pointerType] = true;
+ if (count == 1)
+ check_PointerEvent(event);
+
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerType, "pen", "Test should be run using a pen as input");
+ assert_equals(event.type, "pointerleave", "The " + event.type + " event was received");
+ assert_equals(event.isPrimary, true, "The " + event.type + ".isPrimary is true");
+ assert_true((event.clientX > target0.getBoundingClientRect().left)&&
+ (event.clientX < target0.getBoundingClientRect().right)&&
+ (event.clientY > target0.getBoundingClientRect().top)&&
+ (event.clientY < target0.getBoundingClientRect().bottom),
+ "pointerleave should be received inside of target bounds");
+ });
+ if (count >= 2)
+ test_pointerEvent.done(); // complete test
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Dispatch pointerleave (pen)</h1>
+ <h4>
+ Test Description:
+ When a pointing device that supports hover (pen stylus) leaves the range of the digitizer while over an element, the pointerleave event must be dispatched. In addition, all the pointer events' isPrimary should always be true when the pointing device leaves and enters the range of the digitizer again.
+ </h4>
+ <br />
+ <div id="target0">
+ Use a pen to hover over then lift up away from this element, and repeat it again.
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointermove.html b/testing/web-platform/tests/pointerevents/pointerevent_pointermove.html
new file mode 100644
index 0000000000..c7e798b087
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointermove.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointermove</title>
+ <meta name="viewport" content="width=device-width">
+ <meta name="assert" content="When a pointer changes coordinates, the pointermove event must be dispatched."/>
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>PointerMove</h2>
+ <h4>Test Description: This test checks if pointermove event triggers. Move your mouse over the black rectangle or slide it if you are using touchscreen.</h4>
+ <div id="target0" style="background:black"></div>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointermove = async_test("pointermove event received");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ // When a pointer changes coordinates, the pointermove event must be dispatched.
+ // TA: 5.3
+ on_event(target0, "pointermove", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointermove.done();
+ });
+ });
+
+ // Inject the inputs to run this test.
+ actions_promise = new test_driver.Actions().pointerMove(0, 0, {origin: target0}).send();
+ }
+ </script>
+ <h1>Pointer Events pointermove Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointermove_after_pointerup_target_removed.html b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_after_pointerup_target_removed.html
new file mode 100644
index 0000000000..7ece87237c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_after_pointerup_target_removed.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>After pointerup target is removed, selection should not be updated by pointer move</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>
+"use strict";
+
+/**
+ * This behavior is not defined in any spec, but even if browser supports
+ * expanding selection across text nodes with dragging a pointer, it should
+ * not work after pointerup whose target is removed by a pointerup event
+ * listener.
+ */
+
+addEventListener("load", async () => {
+ const span1 = document.querySelector("span");
+ const span2 = span1.nextSibling;
+ const span3 = span2.nextSibling;
+ const button = document.querySelector("button");
+
+ promise_test(async () => {
+ const overlay = document.createElement("div");
+ overlay.className = "overlay";
+ overlay.addEventListener("pointerup", () => {
+ overlay.remove();
+ });
+ document.body.appendChild(overlay);
+ overlay.style.display = "block";
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+ await new test_driver.Actions()
+ .pointerMove(10, 10, {origin: span1})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(10, 10, {origin: span2})
+ .addTick()
+ .pointerMove(10, 10, {origin: span3})
+ .addTick()
+ .send();
+ assert_true(getSelection().isCollapsed);
+ }, "pointermove after pointerup which deletes the overlay should not keep expanding selection");
+
+ promise_test(async () => {
+ const overlay = document.createElement("div");
+ overlay.className = "overlay";
+ overlay.addEventListener("pointerup", () => {
+ button.focus();
+ overlay.remove();
+ });
+ document.body.appendChild(overlay);
+ overlay.style.display = "block";
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+ await new test_driver.Actions()
+ .pointerMove(10, 10, {origin: span1})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(10, 10, {origin: span2})
+ .addTick()
+ .pointerMove(10, 10, {origin: span3})
+ .addTick()
+ .send();
+ assert_true(getSelection().isCollapsed);
+ }, "pointermove after pointerup which deletes the overlay and move focus to the button should not keep expanding selection");
+}, {once: true});
+</script>
+<style>
+div.overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: none;
+}
+#container {
+ font-size: 32px;
+}
+</style>
+</head>
+<body>
+ <div id="container">
+ <span>abc</span><span>def</span><span>ghi</span><br>
+ <button>button</button>
+ </div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html
new file mode 100644
index 0000000000..3073076a49
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: pointermove has same isPrimary as last pointerdown with the same pointerId</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="The isPrimary attribute of a pointermove event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <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>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointermove has same isPrimary as last pointerdown"); // set up test harness
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointermove_event = null;
+ var pointerdown_event = null;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ on_event(target0, "pointermove", function (event) {
+ if (pointerdown_event != null) {
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointermove.pointerId should be the same as pointerdown.pointerId.");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointermove.isPrimary should be the same as pointerdown.isPrimary.");
+ });
+ pointermove_event = event;
+ }
+ });
+
+ on_event(target0, "pointerdown", function (event) {
+ pointerdown_event = event;
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, "pointerup", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointermove_event, null, "pointermove event was never received: ");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Dispatch a mouse drag in target0.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(3, 3, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: pointermove has the same isPrimary as last pointerdown with the same pointerId</h1>
+ <h4>Test Description:
+ The isPrimary attribute of a pointermove event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute.
+ </h4>
+ <div id="instructions">
+ Press and hold a mouse button, touch contact or pen contact on this element. Move around inside the element while maintaining contact/button down. Only use one device per test run.
+ <p>Lift off of the element to complete the test.</p>
+ </div>
+ <div id="target0" style="touch-action: none;">
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointermove_on_chorded_mouse_button.html b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_on_chorded_mouse_button.html
new file mode 100644
index 0000000000..b5c1803f3d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_on_chorded_mouse_button.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointermove on button state changes</title>
+ <meta name="viewport" content="width=device-width">
+ <meta name="assert" content="When a pointer changes button state and does not produce a different event, the pointermove event must be dispatched."/>
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>PointerMove</h2>
+ <h4>Test Description: This test checks if pointermove event are triggered by button state changes
+ <ol>
+ <li>Put your mouse over the black rectangle</li>
+ <li>Press a button and hold it</li>
+ <li>Press a second button</li>
+ <li>Release the second button</li>
+ <li>Release the first button to complete the test</li>
+ </ol>
+ </h4>
+ <div id="target0" style="background:black"></div>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointermove = async_test("pointermove events received for button state changes");
+ add_completion_callback(showPointerTypes);
+
+ var step = 0;
+ var firstButton = 0;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ // When a pointer changes button state and the circumstances produce no other pointer event, the pointermove event must be dispatched.
+ // 5.2.6
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointermove.step(function() {assert_equals(step, 0, "There must not be more than one pointer down event.");});
+ if (step == 0) {
+ step = 1;
+ firstButton = event.buttons;
+ }
+ });
+ on_event(target0, "pointermove", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (step == 1 && event.button != -1) { // second button pressed
+ test_pointermove.step(function() {assert_not_equals(event.buttons, firstButton, "The pointermove event must be triggered by pressing a second button.");});
+ test_pointermove.step(function() {assert_true((event.buttons & firstButton) != 0, "The first button must still be reported pressed.");});
+ step = 2;
+ } else if (step == 2 && event.button != -1) { // second button released
+ test_pointermove.step(function() {assert_equals(event.buttons, firstButton, "The pointermove event must be triggered by releasing the second button.");});
+ step = 3;
+ }
+ });
+ on_event(target0, "pointerup", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointermove.step(function() {assert_equals(step, 3, "The pointerup event must be triggered after pressing and releasing the second button.");});
+ test_pointermove.step(function() {assert_equals(event.buttons, 0, "The pointerup event must be triggered by releasing the last pressed button.");});
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointermove.done();
+ });
+ });
+
+ // Click on both left and middle buttons.
+ var actions = new test_driver.Actions();
+ actions_promise = actions.pointerMove(0, 0, {origin: target0, button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+ </script>
+ <h1>Pointer Events pointermove on button state changes Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerout_after_pointercancel_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_after_pointercancel_touch.html
new file mode 100644
index 0000000000..782289384a
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_after_pointercancel_touch.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerout</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h2>pointerout</h2>
+ <h4>Test Description: This test checks if pointerout event triggers after pointercancel. Start touch on the black rectangle and move your touch to scroll in any direction. </h4>
+ <p>Note: this test is for touch devices only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerout = async_test("pointerout event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointercancel_event = null;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ on_event(target0, "pointercancel", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ pointercancel_event = event;
+ });
+
+ // After firing the pointercancel event the pointerout event must be dispatched.
+ // TA: 4.3
+ on_event(target0, "pointerout", function (event) {
+ if(event.pointerType == 'touch') {
+ if(pointercancel_event != null) {
+ test_pointerout.step(function() {
+ assert_equals(event.pointerType, pointercancel_event.pointerType, "pointerType is same for pointercancel and pointerout");
+ assert_equals(event.isPrimary, pointercancel_event.isPrimary, "isPrimary is same for pointercancel and pointerout");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerout.done();
+ });
+ }
+ else {
+ test_pointerout.step(function() {
+ assert_true(false,
+ "pointercancel received before pointerout");
+ }, "pointercancel received before pointerout");
+ }
+ }
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down');
+ }
+
+ </script>
+ <h1>Pointer Events pointerout tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerout_no_pointer_movement.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_no_pointer_movement.html
new file mode 100644
index 0000000000..856f2ce37c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_no_pointer_movement.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+<head>
+<title>The pointerout event should not be fired if the pointer doesn't move</title>
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://github.com/w3c/pointerevents/issues/457">
+<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>
+#target{
+ width:100px;
+ height:100px;
+ background-color:red;
+}
+
+#overlay{
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: rgba(0,0,0,0.2);
+ z-index: 1000;
+ text-align: center;
+ display:none;
+}
+</style>
+</head>
+<body>
+<h1>The pointerout event should not be fired if the pointer doesn't move</h1>
+<h4>
+ Test Description: This test checks if the pointerout event dispatched unexpectedly.
+ <ol>
+ <li>Click on the black rectangle.
+ <li>Don't move mouse after clicking.
+ </ol>
+</h4>
+<p>
+<div id="target"></div>
+<div id="overlay"></div>
+<div id="log"></div>
+<script>
+function waitForAnimationFrame() {
+ return new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ });
+}
+
+promise_test(async () => {
+ const target = document.getElementById("target");
+
+ let out_event_count = 0;
+ target.addEventListener("pointerout", function() {
+ out_event_count++;
+ });
+
+ // Wait for the click event on target element and update display style on
+ // overlay element.
+ const promise = new Promise(resolve => {
+ target.addEventListener("click", async function() {
+ const overlay = document.getElementById("overlay");
+ overlay.style.display= 'block';
+ await waitForAnimationFrame();
+
+ overlay.style.display= 'none'
+ await waitForAnimationFrame();
+
+ resolve();
+ }, { once: true });
+ });
+
+ // Click target.
+ test_driver.click(target);
+ await promise;
+
+ assert_equals(out_event_count, 0, "The pointerout event should not be fired");
+}, "The pointerout event should not be fired if the pointer doesn't move");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerout_pen.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_pen.html
new file mode 100644
index 0000000000..210055e653
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_pen.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerout</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>pointerout</h2>
+ <h4>Test Description: This test checks if pointerout event triggers for pen. Place your pen over the black rectangle and then pull the pen out of the digitizer's detectable range. </h4>
+ <p>Note: this test is for devices that support hover - for pen only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerout = async_test("pointerout event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var isPointerupReceived = false;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ // When a pen stylus leaves the hover range detectable by the digitizer the pointerout event must be dispatched.
+ // TA: 7.2
+ on_event(target0, "pointerout", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ if(event.pointerType == 'pen') {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerout.done();
+ });
+ }
+ else {
+ test_pointerout.step(function() {
+ assert_true(false,
+ "you have to use pen for this test");
+ }, "you have to use pen for this test");
+ }
+ });
+
+ // Inject pen inputs.
+ actions_promise = new test_driver.Actions()
+ .addPointer("PenPointer1", "pen")
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(0, 0)
+ .send();
+ }
+
+ </script>
+ <h1>Pointer Events pointerout tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerout_received_once.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_received_once.html
new file mode 100644
index 0000000000..ebc2e545ce
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerout_received_once.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerout received just once</title>
+ <meta name="timeout" content="long">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>pointerout received just once</h1>
+ <h4>
+ Test Description: This test checks if pointerout event dispatched properly.
+ <ol>
+ <li>Put your mouse over the black rectangle.
+ <li>Move your mouse out of the black rectangle
+ </ol>
+ </h4>
+ <p>
+ <div id="target0" style="background:black"></div>
+ <script>
+ var pointeroutCounter = 0;
+ var detected_pointertypes = {};
+ var actions_promise;
+
+ setup({ explicit_done: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ // When a mouse passes through dispatches one event
+ // TA: 7.4
+ on_event(target0, "pointerover", function (event) {
+ pointeroutCounter = 0;
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, "pointerout", function (event) {
+ pointeroutCounter++;
+
+ step_timeout(function() {
+ test(function() {
+ assert_equals(pointeroutCounter, 1, "pointerout received just once")
+ }, "pointerout received just once");
+ actions_promise.then( () => {
+ done();
+ });
+ }, 5000);
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerMove(0, 0)
+ .send();
+ }
+ </script>
+ <h1>Pointer Events pointerout received once test</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.html
new file mode 100644
index 0000000000..bd24daf617
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerrawupdate</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>pointerrawupdate</h2>
+ <h4>Test Description: This test checks that pointerrawupdate is not dispatched on non-SecureContext. </h4>
+ <p>Move your mouse within the black box.</p>
+ <p>Press left button down and then press middle button while holding down left button. Then release the buttons</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerrawupdate = async_test("pointerrawupdate event received");
+ var actions_promise;
+
+ var pointerrawupdateReceived = false;
+ var pointerdownReceived = false;
+ var pointerrawupdateFromButtonChangeReceived = false;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointerrawupdate", function (event) {
+ pointerrawupdateReceived = true;
+ if (pointerdownReceived && event.button != -1)
+ pointerrawupdateFromButtonChangeReceived = true;
+ });
+ on_event(target0, "pointermove", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_false(pointerrawupdateReceived,
+ "Pointerrawupdate event should not have been received before pointermove.");
+ }, "Pointerrawupdate event should have been received before pointermove.");
+ });
+ on_event(target0, "pointerdown", function (event) {
+ pointerdownReceived = true;
+ });
+ on_event(target0, "pointerup", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_false(pointerrawupdateFromButtonChangeReceived,
+ "Pointerrawupdate event should not have been received from chorded button changes.");
+ }, "Pointerrawupdate event should have been received from chorded button changes.");
+
+ test_pointerrawupdate.step(function() {
+ assert_false("onpointerrawupdate" in window,
+ "Window should not have event handler onpointerrawupdate");
+ assert_false("onpointerrawupdate" in window.document,
+ "Document should not have event handler onpointerrawupdate");
+ assert_false("onpointerrawupdate" in window.document.documentElement,
+ "Element should not have event handler onpointerrawupdate");
+ }, "onpointerrawupdate should be exposed only in SecureContext");
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerrawupdate.done();
+ });
+ });
+ var actions = new test_driver.Actions();
+ actions_promise = actions.pointerMove(0, 0, {origin: target0, button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ </script>
+ <div id="complete-notice">
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.https.html b/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.https.html
new file mode 100644
index 0000000000..74c14d932f
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_pointerrawupdate.https.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerrawupdate</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>pointerrawupdate</h2>
+ <h4>Test Description: This test checks if pointerrawupdate is dispatched correctly. </h4>
+ <p>Move your mouse within the black box.</p>
+ <p>Press left button down and then press middle button while holding down left button. Then release the buttons</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerrawupdate = async_test("pointerrawupdate event received");
+ var actions_promise;
+
+ var pointerrawupdateReceived = false;
+ var pointerdownReceived = false;
+ var pointerrawupdateFromButtonChangeReceived = false;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointerrawupdate", function (event) {
+ pointerrawupdateReceived = true;
+ if (pointerdownReceived && event.button != -1)
+ pointerrawupdateFromButtonChangeReceived = true;
+ });
+ on_event(target0, "pointermove", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_true(pointerrawupdateReceived,
+ "Pointerrawupdate event should have been received before pointermove.");
+ }, "Pointerrawupdate event should have been received before pointermove.");
+ });
+ on_event(target0, "pointerdown", function (event) {
+ pointerdownReceived = true;
+ });
+ on_event(target0, "pointerup", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_true(pointerrawupdateFromButtonChangeReceived,
+ "Pointerrawupdate event should have been received from chorded button changes.");
+ }, "Pointerrawupdate event should have been received from chorded button changes.");
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerrawupdate.done();
+ });
+ });
+ var actions = new test_driver.Actions();
+ actions_promise = actions.pointerMove(0, 0, {origin: target0, button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ </script>
+ <div id="complete-notice">
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_events_to_original_target.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_events_to_original_target.html
new file mode 100644
index 0000000000..325b58c85c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_events_to_original_target.html
@@ -0,0 +1,151 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <meta name="variant" content="?mouse">
+ <meta name="variant" content="?touch">
+ <meta name="variant" content="?pen">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="After invoking the releasePointerCapture method on an element, subsequent events for the specified pointer must follow normal hit testing mechanisms for determining the event target"/>
+ <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>
+ <script type="text/javascript">
+ var inputSource = location.search.substring(1);
+ var test_pointerEvent;
+ var detected_pointertypes = {};
+ var captured_event = null;
+ var test_done = false;
+ var overEnterEventsFail = false;
+ var outLeaveEventsFail = false;
+ var f_gotPointerCapture = false;
+ var f_lostPointerCapture = false;
+ var actions_promise;
+
+ function resetTestState() {
+ captured_event = null;
+ test_done = false;
+ overEnterEventsFail = false;
+ outLeaveEventsFail = false;
+ f_gotPointerCapture = false;
+ f_lostPointerCapture = false;
+ }
+
+ function listenerEventHandler(event) {
+ if (test_done)
+ return;
+ detected_pointertypes[event.pointerType] = true;
+ if (event.type == "gotpointercapture") {
+ f_gotPointerCapture = true;
+ check_PointerEvent(event);
+ }
+ else if (event.type == "lostpointercapture") {
+ f_lostPointerCapture = true;
+ f_gotPointerCapture = false;
+ check_PointerEvent(event);
+ }
+ else if(event.type == "pointerover" || event.type == "pointerenter") {
+ if(captured_event && !overEnterEventsFail) {
+ test(function() {
+ assert_false(f_gotPointerCapture, "pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
+ }, expectedPointerType + " pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
+ overEnterEventsFail = true;
+ }
+ }
+ else if(event.type == "pointerout" || event.type == "pointerleave") {
+ if(!outLeaveEventsFail) {
+ test(function() {
+ assert_true(f_lostPointerCapture, "pointerout/leave should not be received unless the target just lost the capture.");
+ }, expectedPointerType + " pointerout/leave should not be received unless the target just lost the capture.");
+ outLeaveEventsFail = true;
+ }
+ }
+ else if (event.pointerId == captured_event.pointerId) {
+ if (f_gotPointerCapture && event.type == "pointermove") {
+ // on first event received for capture, release capture
+ listener.releasePointerCapture(event.pointerId);
+ }
+ else {
+ // if any other events are received after releaseCapture, then the test fails
+ test(function () {
+ assert_unreached(event.target.id + "-" + event.type + " should be handled by target element handler");
+ }, expectedPointerType + " No other events should be recieved by capturing node after release");
+ }
+ }
+ }
+
+ function targetEventHandler(event) {
+ if (test_done)
+ return;
+ if (f_gotPointerCapture) {
+ if(event.type != "pointerout" && event.type != "pointerleave") {
+ test(function () {
+ assert_unreached("The Target element should not have received any events while capture is active. Event recieved:" + event.type + ". ");
+ }, expectedPointerType + " The target element should not receive any events while capture is active");
+ }
+ }
+
+ if (event.type == "pointerdown") {
+ // pointerdown event received will be used to capture events.
+ listener.setPointerCapture(event.pointerId);
+ captured_event = event;
+ }
+
+ if (f_lostPointerCapture) {
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerId, captured_event.pointerId, "pointerID is same for event captured and after release");
+ });
+ if (event.type == "pointerup") {
+ test_done = true;
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+ }
+ }
+
+ function run() {
+ test_pointerEvent = setup_pointerevent_test("got/lost pointercapture: subsequent events to target", [inputSource]); // set up test harness
+ var listener = document.getElementById("listener");
+ var target0 = document.getElementById("target0");
+ target0.style["touchAction"] = "none";
+
+ // target0 and listener - handle all events
+ for (var i = 0; i < All_Pointer_Events.length; i++) {
+ on_event(target0, All_Pointer_Events[i], targetEventHandler);
+ on_event(listener, All_Pointer_Events[i], listenerEventHandler);
+ }
+
+ // Inject pointer inputs.
+ actions_promise = pointerDragInTarget(inputSource, target0, 'right');
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h2 id="pointerTypeDescription"></h2>
+ <div id="listener"></div>
+ <h1>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</h1>
+ <h4>
+ Test Description:
+ Use your pointer and press down in the black box. Then move around in the box and release your pointer.
+ After invoking the releasePointerCapture method on an element, subsequent events for the specified
+ pointer must follow normal hit testing mechanisms for determining the event target.
+ </h4>
+ <br />
+ <div id="target0">
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_invalid_pointerid.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_invalid_pointerid.html
new file mode 100644
index 0000000000..71de198089
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_invalid_pointerid.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: releasePointerCapture DOMException - NotFoundError</title>
+ <meta name="timeout" content="long">
+ <meta name="assert" content="releasePointerCapture DOMException - NotFoundError"/>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://www.w3.org/wiki/PointerEvents/TestAssertions">
+ <meta name="assert" content="When the releasePointerCapture method is invoked, if the provided pointerId value does not match any of the active pointers, a DOMException with the name NotFoundError must be thrown."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <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>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("releasePointerCapture: DOMException NotFoundError"); // set up test harness
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var INVALID_POINTERID = 314159265358973923;
+ var actions_promise;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ target0.style["touchAction"] = "none";
+ var listener = document.getElementById("listener");
+
+ // try to release pointer capture with an invalid id
+ on_event(listener, "pointermove", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ test_pointerEvent.step(function() {
+ assert_throws_dom("NotFoundError", function(){ listener.releasePointerCapture(INVALID_POINTERID); },
+ "It should not be possible to release capture an invalid pointer id");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // set pointer capture
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ listener.setPointerCapture(event.pointerId);
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <div id="listener"></div>
+ <h1> Pointer Event: releasePointerCapture() DOMException - NotFoundError</h1>
+ <h4>
+ Test Description:
+ Upon invocation of the releasePointerCapture method, if the provided pointerId value does not match any of the
+ active pointers, a DOMException with the name NotFoundError must be thrown.
+ </h4>
+ <br />
+ <div id="target0">
+ Use the mouse, touch or pen to move over or contact this box.
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch.html
new file mode 100644
index 0000000000..c54ee2a9d4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Release capture on pointercancel</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable">
+ <h1>Pointer Events Capture Test - release capture on pointercancel</h1>
+ <h4>
+ Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
+ <ol>
+ <li> Touch black rectangle and do not release your touch
+ <li> Move your touch to scroll the page. "lostpointercapture" should be logged inside of the black rectangle immediately after "pointercancel"
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+
+ <script type='text/javascript'>
+ var pointercancelGot = false;
+ var count=0;
+ var event_log = [];
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointer capture is released on pointercancel");
+
+ var target0 = document.getElementById('target0');
+ var actions_promise;
+
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ window.onload = function() {
+ on_event(target0, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ test_pointerEvent.step(function () {
+ assert_equals(e.pointerType, "touch", "Test should be run using a touch as input");
+ });
+ isPointerCapture = true;
+ sPointerCapture(e);
+ pointercancelGot = false;
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ // If the setPointerCapture method has been invoked on the pointer specified by pointerId, and the releasePointerCapture method has not been invoked, a lostpointercapture event must be dispatched to the element on which the setPointerCapture method was invoked. Furthermore, subsequent events for the specified pointer must follow normal hit testing mechanisms for determining the event target.
+ // TA: 4.4
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ test_pointerEvent.step(function () {
+ assert_true(pointercancelGot, "pointercancel was received before lostpointercapture");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ on_event(target0, 'pointercancel', function(e) {
+ event_log.push('pointercancel@target0');
+ pointercancelGot = true;
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down');
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointerup_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointerup_mouse.html
new file mode 100644
index 0000000000..e86e5ab7ca
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_onpointerup_mouse.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Release capture on pointerup</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events Capture Test - release capture on pointerup</h1>
+ <h4>
+ Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
+ <ol>
+ <li> Press and hold left mouse button over "Set Capture" button
+ <li> Release left mouse button anywhere over the document. "lostpointercapture" should be logged inside of the black rectangle immediately after "pointerup"
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var isPointerCapture = false;
+ var pointerupGot = false;
+ var count=0;
+ var event_log = [];
+ var actions_promise;
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var captureButton = document.getElementById('btnCapture');
+
+ setup({ explicit_done: true });
+
+ window.onload = function() {
+ on_event(captureButton, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ if(isPointerCapture == false) {
+ isPointerCapture = true;
+ sPointerCapture(e);
+ pointerupGot = false;
+ }
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ // If the setPointerCapture method has been invoked on the pointer specified by pointerId,
+ // and the releasePointerCapture method has not been invoked,a lostpointercapture event must be
+ // dispatched to the element on which the setPointerCapture method was invoked. Furthermore,
+ // subsequent events for the specified pointer must follow normal hit testing mechanisms for
+ // determining the event target.
+ // TA: 3.7
+ on_event(target0, 'lostpointercapture', function(e) {
+ test(function() {
+ assert_true(pointerupGot, "pointerup was received before lostpointercapture")
+ }, "pointerup was received before lostpointercapture");
+ event_log.push('lostpointercapture@target0');
+ isPointerCapture = false;
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ done();
+ });
+ });
+
+ on_event(target0, 'pointerup', function(e) {
+ event_log.push('pointerup@target0');
+ pointerupGot = true;
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_mouse.html
new file mode 100644
index 0000000000..bdb64e502f
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_mouse.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+ <head>
+ <title>releasePointerCapture on pointerup</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events Capture Test - releasePointerCapture on pointerup</h1>
+ <h4>
+ Test Description: This test checks if releasePointerCapture works properly. Complete the following actions:
+ <ol>
+ <li> Press and hold left mouse button over "Set Capture" button
+ <li> Release left mouse button anywhere over the document
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var isPointerCapture = false;
+ var pointerupGot = false;
+ var count=0;
+ var event_log = [];
+ var actions_promise;
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var captureButton = document.getElementById('btnCapture');
+
+ setup({ explicit_done: true });
+
+ window.onload = function() {
+ on_event(captureButton, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ if(isPointerCapture == false) {
+ isPointerCapture = true;
+ sPointerCapture(e);
+ pointerupGot = false;
+ }
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ test(function() {
+ assert_true(pointerupGot, "pointerup was received before lostpointercapture")
+ }, "pointerup was received before lostpointercapture");
+ event_log.push('lostpointercapture@target0');
+ isPointerCapture = false;
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ done();
+ });
+ });
+
+ on_event(target0, 'pointerup', function(e) {
+ event_log.push('pointerup@target0');
+ var thrown = false;
+ try {
+ target0.releasePointerCapture(e.pointerId);
+ } catch(e) {
+ thrown = true;
+ }
+ test(function() {
+ assert_false(thrown, "target0.releasePointerCapture should not throw");
+ }, "target0.releasePointerCapture should not throw");
+ pointerupGot = true;
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_touch.html
new file mode 100644
index 0000000000..ce730492b4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_pointerup_touch.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+ <head>
+ <title>releasePointerCapture on pointerup</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable">
+ <h1>Pointer Events Capture Test - releasePointerCapture on pointerup</h1>
+ <h4>
+ Test Description: This test checks if releaseCapture works properly on pointer up. Complete the following actions:
+ <ol>
+ <li> Touch black rectangle and do not release your touch
+ <li> Move and release your touch anywhere over the document
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+
+ <script type='text/javascript'>
+ var pointerupGot = false;
+ var count=0;
+ var event_log = [];
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("releasePointerCapture on pointerup");
+
+ var target0 = document.getElementById('target0');
+ var actions_promise;
+
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ window.onload = function() {
+ on_event(target0, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ test_pointerEvent.step(function () {
+ assert_equals(e.pointerType, "touch", "Test should be run using a touch as input");
+ });
+ sPointerCapture(e);
+ pointerupGot = false;
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ test_pointerEvent.step(function () {
+ assert_true(pointerupGot, "pointerup was received before lostpointercapture");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ on_event(target0, 'pointerup', function(e) {
+ event_log.push('pointerup@target0');
+ try {
+ target0.releasePointerCapture(e.pointerId);
+ } catch(error) {
+ test_pointerEvent.step(function () {
+ assert_unreached("target0.releasePointerCapture should not throw");
+ });
+ }
+ pointerupGot = true;
+ });
+
+ on_event(target0, 'touchmove', function(e) {
+ // To prevent pointercancel firing.
+ e.preventDefault();
+ });
+
+ on_event(target0, 'pointercancel', function(e) {
+ test_pointerEvent.step(function () {
+ assert_unreached("target0 shouldn't receive pointercancel");
+ });
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down');
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_release_right_after_capture.html b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_release_right_after_capture.html
new file mode 100644
index 0000000000..409951b918
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_releasepointercapture_release_right_after_capture.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Release pointer capture right after setpointercapture</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var actions_promise;
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_setPointerCapture = async_test("Release pointer capture right after setpointercapture");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ target0.setPointerCapture(event.pointerId);
+ target0.releasePointerCapture(event.pointerId);
+ assert_equals(target0.hasPointerCapture(event.pointerId), false, "After target0.releasePointerCapture, target0.hasPointerCapture should be false");
+ });
+
+ on_event(target0, "gotpointercapture", function (event) {
+ test_setPointerCapture.step(function () {
+ assert_true(false, "target0 shouldn't receive gotpointercapture");
+ });
+ });
+
+ on_event(target0, "lostpointercapture", function (event) {
+ test_setPointerCapture.step(function () {
+ assert_true(false, "target0 shouldn't receive lostpointercapture");
+ });
+ });
+
+ on_event(target0, "pointerup", function (event) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_setPointerCapture.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 10, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Release pointer capture right after setpointercapture</h1>
+ <h4>Test Description:
+ When calling releasePointer right after setPointerCapture method is invoked, the pending pointer capture should be cleared and no element should receive gotpointercapture and lostpointercapture events
+ <ol>
+ <li>Press and hold left mouse button over black box
+ <li>Move mouse and release mouse button
+ </ol>
+ </h4>
+ <br>
+ <div id="target0" touch-action:none></div>
+ <div id="target1" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_root_computed_style.html b/testing/web-platform/tests/pointerevents/pointerevent_root_computed_style.html
new file mode 100644
index 0000000000..c3034d475e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_root_computed_style.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style type="text/css">
+ :root {
+ pointer-events: none;
+ }
+</style>
+<html>
+ <body>
+ </body>
+</html>
+<script>
+ test(() => {
+ let cs = window.getComputedStyle(document.documentElement).getPropertyValue("pointer-events");
+ assert_equals(cs, 'none');
+ });
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_root_hit_test.html b/testing/web-platform/tests/pointerevents/pointerevent_root_hit_test.html
new file mode 100644
index 0000000000..b09dfafe69
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_root_hit_test.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style type="text/css">
+ :root {
+ pointer-events: none;
+ }
+</style>
+<html>
+ <body>
+ </body>
+</html>
+<script>
+ test(() => {
+ let element = document.elementFromPoint(50, 50);
+ assert_equals(element, document.documentElement);
+ });
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_click.html b/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_click.html
new file mode 100644
index 0000000000..bfcc8ad54a
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_click.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Event sequence at implicit release on click</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <meta name="variant" content="?mouse">
+ <meta name="variant" content="?touch">
+ <meta name="variant" content="?pen">
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event."/>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var inputSource = location.search.substring(1);
+ var detected_pointertypes = {};
+ var event_log = [];
+ var start_logging = false;
+ var actions_promise;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ event_log = [];
+ start_logging = false;
+ }
+
+ function run() {
+ var test_pointer_event = setup_pointerevent_test("Event sequence at implicit release on click", [inputSource]);
+
+ on_event(document.getElementById("done"), "click", function() {
+ test_pointer_event.step(function () {
+ var expected_events = "pointerup, lostpointercapture, pointerout, pointerleave";
+ assert_equals(event_log.join(", "), expected_events);
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointer_event.done();
+ });
+ });
+
+ var target = document.getElementById("target");
+ var button = document.getElementById("done");
+
+ All_Pointer_Events.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type == "pointerdown") {
+ event.target.setPointerCapture(event.pointerId);
+
+ } else if (event.type == "gotpointercapture") {
+ start_logging = true;
+
+ } else if (event.type != "pointermove" && start_logging) {
+ event_log.push(event.type);
+ }
+ });
+ });
+
+ // Inject pointer inputs.
+ actions_promise = clickInTarget(inputSource, target).then(function() {
+ return clickInTarget(inputSource, button);
+ });
+ }
+ </script>
+ <style>
+ #target {
+ margin: 20px;
+ background-color: black;
+ }
+
+ #done {
+ margin: 20px;
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Event sequence at implicit release on click</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event.
+ </h4>
+ <ol>
+ <li>Click or tap on Black.</li>
+ <li>Click or tap on Green.</li>
+ </ol>
+ <div id="target"></div>
+ <div id="done"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html b/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html
new file mode 100644
index 0000000000..52f9e439a4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Event sequence at implicit release on drag</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a captured pointer is implicitly released after a drag, the boundary events should follow the lostpointercapture event."/>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var event_log = [];
+ var start_logging = false;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ event_log = [];
+ start_logging = false;
+ }
+
+ function run() {
+ var test_pointer_event = setup_pointerevent_test("Event sequence at implicit release on drag", ["touch"]);
+
+ var button = document.getElementById("done");
+ var actions_promise;
+ on_event(document.getElementById("done"), "click", function() {
+ test_pointer_event.step(function () {
+ var expected_events = "pointercancel, lostpointercapture, pointerout, pointerleave";
+ assert_equals(event_log.join(", "), expected_events);
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointer_event.done();
+ });
+ });
+
+ var target = document.getElementById("target");
+
+ All_Pointer_Events.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type == "pointerdown") {
+ event.target.setPointerCapture(event.pointerId);
+
+ } else if (event.type == "gotpointercapture") {
+ start_logging = true;
+
+ } else if (event.type != "pointermove" && start_logging) {
+ event_log.push(event.type);
+ }
+ });
+ });
+
+ // Inject touch inputs.
+ actions_promise = pointerDragInTarget("touch", target, 'right').then(function() {
+ return clickInTarget("touch", button);
+ });
+ }
+ </script>
+ <style>
+ #target {
+ margin: 20px;
+ background-color: black;
+ touch-action: auto;
+ }
+
+ #done {
+ margin: 20px;
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Event sequence at implicit release on drag</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ When a captured pointer is implicitly released after a drag, the boundary events should follow the lostpointercapture event.
+ </h4>
+ <ol>
+ <li>Drag quickly down starting on Black.</li>
+ <li>Click or tap on Green.</li>
+ </ol>
+ <div id="target"></div>
+ <div id="done"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_disconnected.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_disconnected.html
new file mode 100644
index 0000000000..386de407e9
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_disconnected.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+ <head>
+ <title>setPointerCapture() throws on disconnected node</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_setPointerCapture = async_test("setPointerCapture: DOMException InvalidStateError");
+ var actions_promise;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+
+ target1.parentNode.removeChild(target1);
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[ event.pointerType ] = true;
+ try {
+ target1.setPointerCapture(event.pointerId);
+
+ test_setPointerCapture.step(function() {
+ assert_unreached("DOMException: InvalidStateError should have been thrown.");
+ });
+ } catch (e) {
+ // TA: 13.4
+ test_setPointerCapture.step(function() {
+ assert_equals(e.name, "InvalidStateError", "DOMException should be InvalidStateError");
+ });
+ }
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_setPointerCapture.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: DOMException InvalidStateError</h1>
+ <h4>Test Description:
+ When the setPointerCapture method is invoked, if the target node does not participate in its ownerDocument's tree, a DOMException with the name InvalidStateError must be thrown.
+ </h4>
+ <br>
+ <div id="target0">
+ Use the mouse, touch or pen to contact this box.
+ </div>
+ <div id="target1"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html
new file mode 100644
index 0000000000..f24661aadb
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+ <head>
+ <title>setPointerCapture + inactive button state</title>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h1>setPointerCapture</h1>
+ <h4>
+ Test Description: This test checks if setPointerCapture works properly.
+ <ol>
+ <li>Put your mouse over the black rectangle
+ <li>Move you mouse out to complete the test
+ </ol>
+ </h4>
+ <p>
+ <div id="target0" style="background:black; color:white;"></div>
+ <script>
+ var detected_pointertypes = {};
+
+ var captureGot = false;
+
+ setup({ single_test: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var actions_promise;
+
+ on_event(target0, "pointerover", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ target0.setPointerCapture(event.pointerId);
+ // After we receive a pointerover event, dispatch a pointer move to move out of target0.
+ // https://github.com/w3c/webdriver/issues/1545
+ actions_promise.then(function() {
+ return new test_driver.Actions().pointerMove(1, 1).send();
+ });
+ });
+
+ // First dispatch a pointer move to target0.
+ actions_promise = new test_driver.Actions().pointerMove(0, 0, {origin: target0}).send();
+
+ // When the setPointerCapture method is invoked, if the specified pointer is not in active button state, then the method must have no effect on subsequent pointer events.
+ // TA: 13.2
+ on_event(target0, "pointerout", function (event) {
+ assert_false(captureGot, "pointer capture is not set while button state is inactive")
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ done();
+ });
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ captureGot = true;
+ });
+ }
+ </script>
+ <h1>Pointer Events setPointerCapture Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_invalid_pointerid.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_invalid_pointerid.html
new file mode 100644
index 0000000000..70b4f41b34
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_invalid_pointerid.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: gotPointercapture is fired first.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://www.w3.org/wiki/PointerEvents/TestAssertions">
+ <meta name="assert" content="When the setPointerCapture method is invoked, if the provided pointerId value does not match any of the active pointers, a DOMException with the name NotFountError must be thrown." />
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <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>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("setPointerCapture: DOMException NotFoundError"); // set up test harness
+ var actions_promise;
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var INVALID_POINTERID = -39548;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ target0.style["touchAction"] = "none";
+ var listener = document.getElementById("complete-notice");
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointerEvent.step(function() {
+ assert_throws_dom("NotFoundError", function(){ listener.setPointerCapture(INVALID_POINTERID); },
+ "It should not be possible to capture an invalid pointer id");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: DOMException NotFoundError</h1>
+ <h4>Test Description:
+ When the setPointerCapture method is invoked, if the provided pointerId value does not match any of the active pointers, a DOMException with the name NotFoundError must be thrown.
+ </h4>
+ <br />
+ <div id="target0">
+ Use the mouse, touch or pen to contact this box.
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_override_pending_capture_element.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_override_pending_capture_element.html
new file mode 100644
index 0000000000..f7c1d42991
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_override_pending_capture_element.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test overriding the pending pointer capture element</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var actions_promise;
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_setPointerCapture = async_test("setPointerCapture: override the pending pointer capture element");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ target0.setPointerCapture(event.pointerId);
+ test_setPointerCapture.step(function () {
+ assert_equals(target0.hasPointerCapture(event.pointerId), true, "Set capture to target0, target0.hasPointerCapture should be true");
+ });
+ target1.setPointerCapture(event.pointerId);
+ test_setPointerCapture.step(function () {
+ assert_equals(target0.hasPointerCapture(event.pointerId), false, "Set capture to target1, target0.hasPointerCapture should be false");
+ assert_equals(target1.hasPointerCapture(event.pointerId), true, "Set capture to target1, target1.hasPointerCapture should be true");
+ });
+ });
+
+ on_event(target0, "gotpointercapture", function (event) {
+ assert_true(false, "target0 shouldn't receive gotpointercapture");
+ });
+
+ on_event(target1, "gotpointercapture", function (event) {
+ assert_true(true, "target1 should receive gotpointercapture");
+ });
+
+ on_event(target1, "pointerup", function (event) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_setPointerCapture.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 10, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Test overriding the pending pointer capture element</h1>
+ <h4>Test Description:
+ After an element setPointerCapture, if another element also setPointerCapture and override it, the old pending pointer capture element shouldn't receive any gotpointercapture or lostpointercapture event
+ <ol>
+ <li>Press and hold left mouse button over black box
+ <li>Move mouse and release mouse button
+ </ol>
+ </h4>
+ <br>
+ <div id="target0" touch-action:none></div>
+ <div id="target1" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_mouse.html
new file mode 100644
index 0000000000..31079d56ce
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_mouse.html
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+ <head>
+ <title>setPointerCapture on pointerup</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events Capture Test - setPointerCapture on pointerup</h1>
+ <h4>
+ Test Description: This test checks if setPointerCapture works properly. Complete the following actions:
+ <ol>
+ <li> Press and hold left mouse button over black rectangle
+ <li> Release left mouse button
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+ <br>
+ <script type='text/javascript'>
+ var count=0;
+ var event_log = [];
+ var actions_promise;
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+
+ setup({ explicit_done: true });
+
+ window.onload = function() {
+ on_event(target0, 'pointerdown', function(e) {
+ event_log.push('pointerdown@target0');
+ detected_pointertypes[e.pointerType] = true;
+ test(function () {
+ assert_equals(event.pointerType, "mouse", "Test should be run using a mouse as input");
+ }, "Test should be run using a mouse as input");
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ test(function () {
+ assert_true(false, "target0 shouldn't receive gotpointercapture");
+ }, "target0 shouldn't receive gotpointercapture");
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ test(function () {
+ assert_true(false, "target0 shouldn't receive lostpointercapture");
+ }, "target0 shouldn't receive lostpointercapture");
+ });
+
+ on_event(target0, 'pointerup', function(e) {
+ event_log.push('pointerup@target0');
+ var thrown = false;
+ try {
+ target0.setPointerCapture(e.pointerId);
+ } catch(e) {
+ thrown = true;
+ }
+ test(function() {
+ assert_false(thrown, "target0.setPointerCapture should not throw");
+ }, "target0.setPointerCapture should not throw");
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_touch.html
new file mode 100644
index 0000000000..8122251a71
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_pointerup_touch.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+ <head>
+ <title>setPointerCapture on pointerup</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable">
+ <h1>Pointer Events Capture Test - setPointerCapture on pointerup</h1>
+ <h4>
+ Test Description: This test checks if releaseCapture works properly on pointer up. Complete the following actions:
+ <ol>
+ <li> Touch black rectangle
+ <li> Release your touch
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+
+ <script type='text/javascript'>
+ var count=0;
+ var event_log = [];
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("setPointerCapture on pointerup");
+
+ var target0 = document.getElementById('target0');
+ var actions_promise;
+
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ window.onload = function() {
+ on_event(target0, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ event_log.push('pointerdown@target0');
+ test_pointerEvent.step(function () {
+ assert_equals(e.pointerType, "touch", "Test should be run using a touch as input");
+ });
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ });
+
+ on_event(target0, 'pointerup', function(e) {
+ event_log.push('pointerup@target0');
+ try {
+ target0.setPointerCapture(e.pointerId);
+ } catch(error) {
+ test_pointerEvent.step(function () {
+ assert_unreached("target0.setPointerCapture should not throw");
+ });
+ }
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ });
+
+ on_event(target0, 'touchmove', function(e) {
+ // To prevent pointercancel firing.
+ e.preventDefault();
+ });
+
+ on_event(target0, 'pointercancel', function(e) {
+ test_pointerEvent.step(function () {
+ assert_unreached("target0 shouldn't receive pointercancel");
+ });
+ });
+
+ // Inject touch inputs.
+ actions_promise = new test_driver.Actions()
+ .addPointer("touchPointer1", "touch")
+ .pointerMove(10, 10, {origin: target0})
+ .pointerDown()
+ .pause(100)
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_relatedtarget.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_relatedtarget.html
new file mode 100644
index 0000000000..37de34d5a6
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_relatedtarget.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Set/Release capture + relatedTarget</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>Pointer Events Capture Test - capture and relatedTarget</h1>
+ <h4>
+ Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
+ <ol>
+ <li> Put your mouse over the purple rectangle. pointerover should be received for the purple rectangle
+ <li> Press and hold left mouse button over "Set Capture" button
+ <li> Move your mouse. pointerover should be received for the black rectangle
+ <li> Release left mouse button to complete the test.
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+
+ <div id="target0" style="background:black; color:white"></div>
+ <br>
+ <div id="target1" style="background:purple; color:white"></div>
+ <br>
+ <input type="button" id="btnCapture" value="Set Capture">
+ <script type='text/javascript'>
+ var isPointerCapture = false;
+ var isPointeroverGot = false;
+ var count=0;
+ var event_log = [];
+
+ var detected_pointertypes = {};
+ add_completion_callback(end_of_test);
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var target0 = document.getElementById('target0');
+ var target1 = document.getElementById('target1');
+ var captureButton = document.getElementById('btnCapture');
+
+ setup({ explicit_done: true });
+
+ window.onload = function() {
+ on_event(captureButton, 'pointerdown', function(e) {
+ if(isPointerCapture == false) {
+ isPointerCapture = true;
+ sPointerCapture(e);
+ }
+ else {
+ isPointerCapture = false;
+ rPointerCapture(e);
+ }
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ event_log.push('gotpointercapture@target0');
+ });
+
+ on_event(target0, 'lostpointercapture', function(e) {
+ event_log.push('lostpointercapture@target0');
+ isPointerCapture = false;
+ });
+
+ run();
+ }
+
+ function run() {
+ var actions_promise;
+
+ // After invoking the setPointerCapture method on an element, subsequent pointer events for the specified pointer must be targeted at that element
+ // and boundary events should be sent accordingly and relatedTarget should behave normally.
+ on_event(target0, "pointerover", function (event) {
+ event_log.push('pointerover@target0');
+ if(isPointerCapture && isPointeroverGot) {
+ test(function() {
+ assert_not_equals(event.relatedTarget, null, "relatedTarget should not be null even when the capture is set")
+ }, "relatedTarget should not be null even when the capture is set.");
+
+ actions_promise.then( () => {
+ done();
+ });
+ }
+ });
+
+ on_event(target1, "pointerover", function (event) {
+ detected_pointertypes[ event.pointerType ] = true;
+ if(!isPointeroverGot) {
+ test(function() {
+ assert_equals(isPointerCapture, false, "pointerover shouldn't trigger for this target when capture is enabled");
+ }, "pointerover shouldn't trigger for the purple rectangle while the black rectangle has capture");
+ isPointeroverGot = true;
+ event_log.push('pointerover@target1');
+ }
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerMove(0, 0, {origin: captureButton})
+ .pointerDown()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerMove(0, 0, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_to_same_element_twice.html b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_to_same_element_twice.html
new file mode 100644
index 0000000000..5225ac508d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_setpointercapture_to_same_element_twice.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+ <head>
+ <title>setPointerCapture() to the element which already captured the pointer</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var actions_promise;
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+ var test_setPointerCapture = async_test("setPointerCapture: set to the element which already captured the pointer");
+ var got_pointer_capture = false;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var target1 = document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ target0.setPointerCapture(event.pointerId);
+ });
+
+ on_event(target0, "gotpointercapture", function (event) {
+ test_setPointerCapture.step(function () {
+ assert_equals(got_pointer_capture, false, "Target0 should receive gotpointercapture at the first time it captured the pointer");
+ assert_equals(target0.hasPointerCapture(event.pointerId), true, "Target 0 received gotpointercapture, target0.hasPointerCapture should be true");
+ });
+ got_pointer_capture = true;
+
+ target0.setPointerCapture(event.pointerId);
+ test_setPointerCapture.step(function () {
+ assert_equals(target0.hasPointerCapture(event.pointerId), true, "Set capture to target0, target0.hasPointerCapture should be true");
+ assert_equals(target1.hasPointerCapture(event.pointerId), false, "Set capture to target0, target1.hasPointerCapture should be false");
+ });
+ });
+
+ on_event(target0, "pointerup", function (event) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_setPointerCapture.done();
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 10, {origin: target0})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: setPointerCapture to the element which already captured the pointer</h1>
+ <h4>Test Description:
+ When the setPointerCapture method is invoked, if the target element had already captured the pointer, it should not trigger any gotpointercapture or lostpointercapture event
+ <ol>
+ <li>Press and hold left mouse button over black box
+ <li>Move mouse and release mouse button
+ </ol>
+ </h4>
+ <br>
+ <div id="target0" touch-action:none></div>
+ <div id="target1" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_styles.css b/testing/web-platform/tests/pointerevents/pointerevent_styles.css
new file mode 100644
index 0000000000..1ee3b0b396
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_styles.css
@@ -0,0 +1,112 @@
+#innerFrame {
+position: absolute;
+top: 300px;
+left: 200px;
+height: 100px;
+width: 100px;
+}
+
+.spacer {
+height: 100px;
+}
+
+#square1 {
+top: 330px;
+left: 150px;
+background: black;
+}
+
+#square2 {
+top: 50px;
+left: 30px;
+visibility: hidden;
+background: red;
+}
+
+.square {
+height: 20px;
+width: 20px;
+position: absolute;
+padding: 0px;
+}
+
+#target0 {
+background: black;
+color: white;
+white-space: nowrap;
+overflow-y: auto;
+overflow-x: auto;
+}
+
+#target1 {
+background: purple;
+color: white;
+white-space: nowrap;
+overflow-y: auto;
+overflow-x: auto;
+}
+
+#scrollTarget {
+ background: darkblue;
+}
+
+.touchActionNone {
+touch-action: none;
+}
+
+#innerframe {
+width: 90%;
+margin: 10px;
+margin-left: 10%;
+height: 200px;
+}
+
+.scroller {
+width: 700px;
+height: 430px;
+margin: 20px;
+overflow: auto;
+background: black;
+}
+
+.scroller > div {
+height: 1000px;
+width: 1000px;
+color: white;
+}
+
+.scroller > div div {
+height: 100%;
+width: 100%;
+color: white;
+}
+
+div {
+margin: 0em;
+padding: 2em;
+}
+
+#complete-notice {
+background: #afa;
+border: 1px solid #0a0;
+display: none;
+}
+
+#pointertype-log {
+font-weight: bold;
+}
+
+#event-log {
+font-weight: bold;
+}
+
+#listener {
+background: orange;
+border: 1px solid orange;
+position: absolute;
+top: -100px;
+}
+
+body.scrollable {
+min-height: 5000px;
+}
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_support.js b/testing/web-platform/tests/pointerevents/pointerevent_support.js
new file mode 100644
index 0000000000..f17a76e5f5
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_support.js
@@ -0,0 +1,544 @@
+const All_Pointer_Events = [
+ "pointerdown",
+ "pointerup",
+ "pointercancel",
+ "pointermove",
+ "pointerover",
+ "pointerout",
+ "pointerenter",
+ "pointerleave",
+ "gotpointercapture",
+ "lostpointercapture"
+];
+
+// https://w3c.github.io/pointerevents/#the-button-property
+// Values for the button property, which indicates the device button whose state
+// change fired the event.
+const ButtonChange = {
+ NONE: -1,
+ PEN_CONTACT: 0,
+ TOUCH_CONTACT: 0,
+ LEFT_MOUSE: 0,
+ MIDDLE_MOUSE: 1,
+ RIGHT_MOUSE: 2,
+ X1_MOUSE: 3,
+ X2_MOUSE: 4,
+ PEN_ERASER_BUTTON: 5
+};
+
+// https://w3c.github.io/pointerevents/#the-buttons-property
+// The buttons property gives the current state of the device buttons as a
+// bitmask.
+const ButtonsBitfield = {
+ NONE: 0,
+ PEN_CONTACT: 1,
+ TOUCH_CONTACT: 1,
+ LEFT_MOUSE: 1,
+ RIGHT_MOUSE: 2,
+ PEN_BARREL_BUTTON: 2,
+ MIDDLE_MOUSE: 4,
+ X1_MOUSE: 8,
+ X2_MOUSE: 16,
+ PEN_ERASER_BUTTON: 32
+};
+
+// Check for conformance to PointerEvent interface
+// https://w3c.github.io/pointerevents/#pointerevent-interface
+function check_PointerEvent(event, testNamePrefix, standardAttrs = true) {
+ if (testNamePrefix === undefined)
+ testNamePrefix = "";
+
+ // Use expectedPointerType if set otherwise just use the incoming event pointerType in the test name.
+ var pointerTestName = (testNamePrefix ? testNamePrefix + ' ' : '')
+ + (expectedPointerType == null ? event.pointerType : expectedPointerType) + ' ' + event.type;
+
+ if (standardAttrs) {
+ if (expectedPointerType != null) {
+ test(function () {
+ assert_equals(event.pointerType, expectedPointerType);
+ }, pointerTestName + ".pointerType is correct.");
+ }
+
+ test(function () {
+ assert_true(event instanceof event.target.ownerDocument.defaultView.PointerEvent);
+ }, pointerTestName + " event is a PointerEvent event");
+ }
+
+ // Check attributes for conformance to WebIDL (existence, type, being readable).
+ var idl_type_check = {
+ "long": function (v) { return typeof v === "number" && Math.round(v) === v; },
+ "float": function (v) { return typeof v === "number"; },
+ "string": function (v) { return typeof v === "string"; },
+ "boolean": function (v) { return typeof v === "boolean" },
+ "object": function (v) { return typeof v === "object" }
+ };
+
+ // Check values for inherited attributes.
+ // https://w3c.github.io/pointerevents/#attributes-and-default-actions
+ if (!standardAttrs) {
+ test(function () {
+ assert_implements_optional("fromElement" in event);
+ assert_equals(event.fromElement, null);
+ }, pointerTestName + ".fromElement value is null");
+ test(function () {
+ assert_implements_optional("toElement" in event);
+ assert_equals(event.toElement, null);
+ }, pointerTestName + ".toElement value is null");
+ } else {
+ test(function () {
+ assert_equals(event.isTrusted, true);
+ }, pointerTestName + ".isTrusted value is true");
+ test(function () {
+ let expected = (event.type != 'pointerenter' && event.type != 'pointerleave');
+ assert_equals(event.composed, expected);
+ }, pointerTestName + ".composed value is valid");
+ test(function () {
+ let expected = (event.type != 'pointerenter' && event.type != 'pointerleave');
+ assert_equals(event.bubbles, expected);
+ }, pointerTestName + ".bubbles value is valid");
+ test(function () {
+ let cancelable_events = [
+ 'pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'
+ ];
+ assert_equals(event.cancelable, cancelable_events.includes(event.type));
+ }, pointerTestName + ".cancelable value is valid");
+
+ // Check the pressure value.
+ // https://w3c.github.io/pointerevents/#dom-pointerevent-pressure
+ test(function () {
+ assert_greater_than_equal(event.pressure, 0, "pressure is greater than or equal to 0");
+ assert_less_than_equal(event.pressure, 1, "pressure is less than or equal to 1");
+
+ if (event.buttons === 0) {
+ assert_equals(event.pressure, 0, "pressure is 0 with no buttons pressed");
+ } else {
+ assert_greater_than(event.pressure, 0, "pressure is greater than 0 with a button pressed");
+ if (event.pointerType === "mouse") {
+ assert_equals(event.pressure, 0.5, "pressure is 0.5 for mouse with a button pressed");
+ }
+ }
+ }, pointerTestName + ".pressure value is valid");
+
+ // Check mouse-specific properties.
+ if (event.pointerType === "mouse") {
+ test(function () {
+ assert_equals(event.width, 1, "width of mouse should be 1");
+ assert_equals(event.height, 1, "height of mouse should be 1");
+ assert_equals(event.tiltX, 0, event.type + ".tiltX is 0 for mouse");
+ assert_equals(event.tiltY, 0, event.type + ".tiltY is 0 for mouse");
+ assert_true(event.isPrimary, event.type + ".isPrimary is true for mouse");
+ }, pointerTestName + " properties for pointerType = mouse");
+ }
+
+ // Check "pointerup" specific properties.
+ if (event.type == "pointerup") {
+ test(function () {
+ assert_equals(event.width, 1, "width of pointerup should be 1");
+ assert_equals(event.height, 1, "height of pointerup should be 1");
+ }, pointerTestName + " properties for pointerup");
+ }
+ }
+}
+
+function showPointerTypes() {
+ var complete_notice = document.getElementById("complete-notice");
+ var pointertype_log = document.getElementById("pointertype-log");
+ var pointertypes = Object.keys(detected_pointertypes);
+ pointertype_log.innerHTML = pointertypes.length ?
+ pointertypes.join(",") : "(none)";
+ complete_notice.style.display = "block";
+}
+
+function showLoggedEvents() {
+ var event_log_elem = document.getElementById("event-log");
+ event_log_elem.innerHTML = event_log.length ? event_log.join(", ") : "(none)";
+
+ var complete_notice = document.getElementById("complete-notice");
+ complete_notice.style.display = "block";
+}
+
+function failOnScroll() {
+ assert_true(false,
+ "scroll received while shouldn't");
+}
+
+function updateDescriptionNextStep() {
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll text RIGHT.";
+}
+
+function updateDescriptionComplete() {
+ document.getElementById('desc').innerHTML = "Test Description: Test complete";
+}
+
+function updateDescriptionSecondStepTouchActionElement(target, scrollReturnInterval) {
+ window.step_timeout(function() {
+ objectScroller(target, 'up', 0);}
+ , scrollReturnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element RIGHT moving your outside of the red border";
+}
+
+function updateDescriptionThirdStepTouchActionElement(target, scrollReturnInterval, callback = null) {
+ window.step_timeout(function() {
+ objectScroller(target, 'left', 0);
+ if (callback) {
+ callback();
+ }
+ }, scrollReturnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the element. Then tap complete button";
+}
+
+function updateDescriptionFourthStepTouchActionElement(target, scrollReturnInterval) {
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element RIGHT starting your touch inside of the element";
+}
+
+function objectScroller(target, direction, value) {
+ if (direction == 'up') {
+ target.scrollTop = 0;
+ } else if (direction == 'left') {
+ target.scrollLeft = 0;
+ }
+}
+
+function sPointerCapture(e) {
+ try {
+ target0.setPointerCapture(e.pointerId);
+ }
+ catch(e) {
+ }
+}
+
+function rPointerCapture(e) {
+ try {
+ captureButton.value = 'Set Capture';
+ target0.releasePointerCapture(e.pointerId);
+ }
+ catch(e) {
+ }
+}
+
+var globalPointerEventTest = null;
+var expectedPointerType = null;
+const ALL_POINTERS = ['mouse', 'touch', 'pen'];
+
+function MultiPointerTypeTest(testName, types) {
+ this.testName = testName;
+ this.types = types;
+ this.currentTypeIndex = 0;
+ this.currentTest = null;
+ this.createNextTest();
+}
+
+MultiPointerTypeTest.prototype.step = function(op) {
+ this.currentTest.step(op);
+}
+
+MultiPointerTypeTest.prototype.skip = function() {
+ var prevTest = this.currentTest;
+ this.createNextTest();
+ prevTest.timeout();
+}
+
+MultiPointerTypeTest.prototype.done = function() {
+ if (this.currentTest.status != 1) {
+ var prevTest = this.currentTest;
+ this.createNextTest();
+ if (prevTest != null)
+ prevTest.done();
+ }
+}
+
+MultiPointerTypeTest.prototype.step = function(stepFunction) {
+ this.currentTest.step(stepFunction);
+}
+
+MultiPointerTypeTest.prototype.createNextTest = function() {
+ if (this.currentTypeIndex < this.types.length) {
+ var pointerTypeDescription = document.getElementById('pointerTypeDescription');
+ document.getElementById('pointerTypeDescription').innerHTML = "Follow the test instructions with <span style='color: red'>" + this.types[this.currentTypeIndex] + "</span>. If you don't have the device <a href='javascript:;' onclick='globalPointerEventTest.skip()'>skip it</a>.";
+ this.currentTest = async_test(this.types[this.currentTypeIndex] + ' ' + this.testName);
+ expectedPointerType = this.types[this.currentTypeIndex];
+ this.currentTypeIndex++;
+ } else {
+ document.getElementById('pointerTypeDescription').innerHTML = "";
+ }
+ resetTestState();
+}
+
+function setup_pointerevent_test(testName, supportedPointerTypes) {
+ return globalPointerEventTest = new MultiPointerTypeTest(testName, supportedPointerTypes);
+}
+
+function checkPointerEventType(event) {
+ assert_equals(event.pointerType, expectedPointerType, "pointerType should be the same as the requested device.");
+}
+
+function touchScrollInTarget(target, direction) {
+ var x_delta = 0;
+ var y_delta = 0;
+ if (direction == "down") {
+ x_delta = 0;
+ y_delta = -10;
+ } else if (direction == "up") {
+ x_delta = 0;
+ y_delta = 10;
+ } else if (direction == "right") {
+ x_delta = -10;
+ y_delta = 0;
+ } else if (direction == "left") {
+ x_delta = 10;
+ y_delta = 0;
+ } else {
+ throw("scroll direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
+ }
+ return new test_driver.Actions()
+ .addPointer("touchPointer1", "touch")
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(x_delta, y_delta, {origin: target})
+ .pointerMove(2 * x_delta, 2 * y_delta, {origin: target})
+ .pointerMove(3 * x_delta, 3 * y_delta, {origin: target})
+ .pointerMove(4 * x_delta, 4 * y_delta, {origin: target})
+ .pointerMove(5 * x_delta, 5 * y_delta, {origin: target})
+ .pointerMove(6 * x_delta, 6 * y_delta, {origin: target})
+ .pause(100)
+ .pointerUp()
+ .send();
+}
+
+function clickInTarget(pointerType, target) {
+ var pointerId = pointerType + "Pointer1";
+ return new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerUp()
+ .send();
+}
+
+function rightClickInTarget(pointerType, target) {
+ let pointerId = pointerType + "Pointer1";
+ let actions = new test_driver.Actions();
+ return actions.addPointer(pointerId, pointerType)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown({button:actions.ButtonType.RIGHT})
+ .pointerUp({button:actions.ButtonType.RIGHT})
+ .send();
+}
+
+function twoFingerDrag(target) {
+ return new test_driver.Actions()
+ .addPointer("touchPointer1", "touch")
+ .addPointer("touchPointer2", "touch")
+ .pointerMove(0, 0, { origin: target, sourceName: "touchPointer1" })
+ .pointerMove(10, 0, { origin: target, sourceName: "touchPointer2" })
+ .pointerDown({ sourceName: "touchPointer1" })
+ .pointerDown({ sourceName: "touchPointer2" })
+ .pointerMove(0, 10, { origin: target, sourceName: "touchPointer1" })
+ .pointerMove(10, 10, { origin: target, sourceName: "touchPointer2" })
+ .pointerMove(0, 20, { origin: target, sourceName: "touchPointer1" })
+ .pointerMove(10, 20, { origin: target, sourceName: "touchPointer2" })
+ .pause(100)
+ .pointerUp({ sourceName: "touchPointer1" })
+ .pointerUp({ sourceName: "touchPointer2" })
+ .send();
+}
+
+function pointerDragInTarget(pointerType, target, direction) {
+ var x_delta = 0;
+ var y_delta = 0;
+ if (direction == "down") {
+ x_delta = 0;
+ y_delta = 10;
+ } else if (direction == "up") {
+ x_delta = 0;
+ y_delta = -10;
+ } else if (direction == "right") {
+ x_delta = 10;
+ y_delta = 0;
+ } else if (direction == "left") {
+ x_delta = -10;
+ y_delta = 0;
+ } else {
+ throw("drag direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
+ }
+ var pointerId = pointerType + "Pointer1";
+ return new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(x_delta, y_delta, {origin: target})
+ .pointerMove(2 * x_delta, 2 * y_delta, {origin: target})
+ .pointerMove(3 * x_delta, 3 * y_delta, {origin: target})
+ .pointerUp()
+ .send();
+}
+
+function pointerHoverInTarget(pointerType, target, direction) {
+ var x_delta = 0;
+ var y_delta = 0;
+ if (direction == "down") {
+ x_delta = 0;
+ y_delta = 10;
+ } else if (direction == "up") {
+ x_delta = 0;
+ y_delta = -10;
+ } else if (direction == "right") {
+ x_delta = 10;
+ y_delta = 0;
+ } else if (direction == "left") {
+ x_delta = -10;
+ y_delta = 0;
+ } else {
+ throw("drag direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
+ }
+ var pointerId = pointerType + "Pointer1";
+ return new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ .pointerMove(0, 0, {origin: target})
+ .pointerMove(x_delta, y_delta, {origin: target})
+ .pointerMove(2 * x_delta, 2 * y_delta, {origin: target})
+ .pointerMove(3 * x_delta, 3 * y_delta, {origin: target})
+ .send();
+}
+
+function moveToDocument(pointerType) {
+ var pointerId = pointerType + "Pointer1";
+ return new test_driver.Actions()
+ .addPointer(pointerId, pointerType)
+ // WebDriver initializes the pointer position (0, 0), therefore, we need
+ // to move different position first. Otherwise, moving to (0, 0) may be
+ // ignored.
+ .pointerMove(1, 1)
+ .pointerMove(0, 0)
+ .send();
+}
+
+// Returns a promise that only gets resolved when the condition is met.
+function resolveWhen(condition) {
+ return new Promise((resolve, reject) => {
+ function tick() {
+ if (condition())
+ resolve();
+ else
+ requestAnimationFrame(tick.bind(this));
+ }
+ tick();
+ });
+}
+
+// Returns a promise that only gets resolved after n animation frames
+function waitForAnimationFrames(n) {
+ let p = 0;
+ function next() {
+ p++;
+ return p === n;
+ }
+ return resolveWhen(next);
+}
+
+function isPointerEvent(eventName) {
+ return All_Pointer_Events.includes(eventName);
+}
+
+function isMouseEvent(eventName) {
+ return ["mousedown", "mouseup", "mousemove", "mouseover",
+ "mouseenter", "mouseout", "mouseleave",
+ "click", "contextmenu", "dblclick"
+ ].includes(eventName);
+}
+
+// Events is a list of events fired at a target.
+//
+// Checks to see if each pointer event has a corresponding mouse event in the
+// event array and the two events are in the proper order (pointer event is
+// first).
+//
+// See https://w3c.github.io/pointerevents/#mapping-for-devices-that-support-hover
+function arePointerEventsBeforeCompatMouseEvents(events) {
+ function arePointerAndMouseEventCompatible(pointerEventName, mouseEventName) {
+ return pointerEventName.startsWith("pointer")
+ && mouseEventName.startsWith("mouse")
+ && pointerEventName.substring(7) === mouseEventName.substring(5);
+ }
+
+ function arePointerAndMouseEventInProperOrder(pointerEventIndex, mouseEventIndex, events) {
+ return (pointerEventIndex < mouseEventIndex && isPointerEvent(events[pointerEventIndex]) && isMouseEvent(events[mouseEventIndex])
+ && arePointerAndMouseEventCompatible(events[pointerEventIndex], events[mouseEventIndex]));
+ }
+
+ let currentPointerEventIndex = events.findIndex((event) => isPointerEvent(event));
+ let currentMouseEventIndex = events.findIndex((event) => isMouseEvent(event));
+
+ while (1) {
+ if (currentMouseEventIndex < 0 && currentPointerEventIndex < 0)
+ return true;
+ if (currentMouseEventIndex < 0 || currentPointerEventIndex < 0)
+ return false;
+ if (!arePointerAndMouseEventInProperOrder(currentPointerEventIndex, currentMouseEventIndex, events))
+ return false;
+
+ let pointerIdx = events.slice(currentPointerEventIndex + 1).findIndex(isPointerEvent);
+ let mouseIdx = events.slice(currentMouseEventIndex + 1).findIndex(isMouseEvent);
+
+ currentPointerEventIndex = (pointerIdx < 0) ? pointerIdx : (currentPointerEventIndex + 1 + pointerIdx);
+ currentMouseEventIndex = (mouseIdx < 0) ? mouseIdx : (currentMouseEventIndex + 1 + mouseIdx);
+ }
+
+ return true;
+}
+
+// Returns a |Promise| that gets resolved with the event object when |target|
+// receives an event of type |event_type|.
+//
+// The optional |test| parameter adds event handler cleanup for the case |test|
+// terminates before the event is received.
+function getEvent(event_type, target, test) {
+ return new Promise(resolve => {
+ const listener = e => resolve(e);
+ target.addEventListener(event_type, listener, { once: true });
+ if (test) {
+ test.add_cleanup(() =>
+ target.removeEventListener(event_type, listener, { 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|.
+//
+// The optional |test| parameter adds event handler cleanup for the case |test|
+// terminates before a matching event is received.
+function getMessageData(message_data_type, source, test) {
+ return new Promise(resolve => {
+ const listener = e => {
+ if (e.source != source || !e.data || e.data.type != message_data_type)
+ return;
+ window.removeEventListener("message", listener);
+ resolve(e.data);
+ }
+
+ window.addEventListener("message", listener);
+ if (test) {
+ test.add_cleanup(() =>
+ window.removeEventListener("message", listener));
+ }
+ });
+}
+
+// The optional |test| parameter adds event handler cleanup for the case |test|
+// terminates before the event is received.
+function preventDefaultPointerdownOnce(target, test) {
+ return new Promise((resolve) => {
+ const listener = e => {
+ e.preventDefault();
+ resolve();
+ }
+
+ target.addEventListener("pointerdown", listener, { once: true });
+ if (test) {
+ test.add_cleanup(() =>
+ target.removeEventListener("pointerdown", listener, { once: true }));
+ }
+ });
+}
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_click.html b/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_click.html
new file mode 100644
index 0000000000..2f99e7a976
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_click.html
@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Suppress compatibility mouse events on click</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a pointerdown is canceled, a click/tap shouldn't fire any compatibility mouse events."/>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var test_pointerEvent = async_test("Suppress compat mouse events on click");
+ add_completion_callback(end_of_test);
+ var actions_promise;
+
+ var detected_pointertypes = {};
+ var event_log = [];
+
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ function end_of_interaction() {
+ test(function () {
+ assert_equals(event_log.join(", "),
+ "click@target0, mousedown@target1, mouseup@target1, click@target1");
+ }, "Event log");
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+
+ function run() {
+ var targetDone = document.getElementById('done');
+ on_event(targetDone, "click", end_of_interaction);
+
+ var target_list = ["target0", "target1"];
+ var pointer_event_list = ["pointerdown"];
+ var mouse_event_list = ["mousedown", "mouseup", "click"];
+
+ target_list.forEach(function(targetId) {
+ var target = document.getElementById(targetId);
+
+ pointer_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ var label = event.type + "@" + targetId;
+
+ test(function () {
+ assert_true(event.isPrimary);
+ }, "primary pointer " + label);
+
+ if (label === "pointerdown@target0")
+ event.preventDefault();
+ });
+ });
+
+ mouse_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ event_log.push(event.type + "@" + targetId);
+ });
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: targetDone})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <style>
+ #target0, #target1 {
+ margin: 20px;
+ }
+
+ #done {
+ margin: 20px;
+ border: 2px solid black;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Suppress compatibility mouse events on click</h1>
+ <h4>
+ When a pointerdown is canceled, a click/tap shouldn't fire any compatibility mouse events except click event.
+ </h4>
+ <ol>
+ <li> Click or tap on Target0.</li>
+ <li> Click or tap on Target1.</li>
+ <li> Click Done.</li>
+ </ol>
+ <div id="target0">
+ Target0
+ </div>
+ <div id="target1">
+ Target1
+ </div>
+ <div id="done">
+ Done
+ </div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
new file mode 100644
index 0000000000..6ed19c2467
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
@@ -0,0 +1,139 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Suppress compatibility mouse events on drag</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a pointerdown is canceled, a mouse drag shouldn't fire any compatibility mouse events."/>
+ <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>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var test_pointerEvent = async_test("Suppress compat mouse events on drag");
+ add_completion_callback(end_of_test);
+ var actions_promise;
+
+ var detected_pointertypes = {};
+ var event_log = [];
+
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ var include_next_mousemove = false;
+
+ // Limits logging/testing of mousemove.
+ function drop_event(event_type) {
+ return (event_type == "mousemove" && !include_next_mousemove);
+ }
+
+ function end_of_interaction() {
+ test(function () {
+ assert_equals(event_log.join(", "),
+ "mousedown@target1, mousemove@target1, mouseup@target1");
+ }, "Event log");
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_pointerEvent.done();
+ });
+ }
+
+ function run() {
+ var targetDone = document.getElementById('done');
+ on_event(targetDone, "click", end_of_interaction);
+
+ var target_list = ["target0", "target1"];
+ var pointer_event_list = ["pointerdown" , "pointermove", "pointerup"];
+ var mouse_event_list = ["mousedown", "mouseup", "mousemove"];
+
+ target_list.forEach(function(targetId) {
+ var target = document.getElementById(targetId);
+
+ pointer_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ var label = event.type + "@" + targetId;
+
+ if (event.type == "pointerdown") {
+ test(function () {
+ assert_true(event.isPrimary);
+ }, "primary pointer " + label);
+ }
+
+ if (label === "pointerdown@target0")
+ event.preventDefault();
+ });
+ });
+
+ mouse_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ if (drop_event(event.type))
+ return;
+
+ event_log.push(event.type + "@" + targetId);
+
+ include_next_mousemove = (event.type == "mousedown");
+ });
+ });
+ });
+
+ // Inject mouse inputs.
+ actions_promise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerUp()
+ .pointerMove(0, 0, {origin: target1})
+ .pointerDown()
+ .pointerMove(10, 0, {origin: target1})
+ .pointerUp()
+ .pointerMove(0, 0, {origin: targetDone})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+ </script>
+ <style>
+ #target0, #target1 {
+ margin: 20px;
+ touch-action: none;
+ }
+
+ #done {
+ margin: 20px;
+ border: 2px solid black;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Suppress compatibility mouse events on drag</h1>
+ <h4>
+ When a pointerdown is canceled, a mouse drag shouldn't fire any compatibility mouse events.
+ </h4>
+ <ol>
+ <li> Drag mouse within Target0 &amp; release.</li>
+ <li> Drag mouse within Target1 &amp; release.</li>
+ <li> Click Done.</li>
+ </ol>
+ <div id="target0">
+ Target0
+ </div>
+ <div id="target1">
+ Target1
+ </div>
+ <div id="done">
+ Done
+ </div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_tiltX_tiltY_to_azimuth_altitude.html b/testing/web-platform/tests/pointerevents/pointerevent_tiltX_tiltY_to_azimuth_altitude.html
new file mode 100644
index 0000000000..4521c247d1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_tiltX_tiltY_to_azimuth_altitude.html
@@ -0,0 +1,109 @@
+<!doctype html>
+<html>
+ <head>
+ <title>TiltX/TiltY to Azimuth/Altitude</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ if(window.test){
+ [{tiltX:0,tiltY:0, azimuth:0, altitude:Math.PI/2},
+ {tiltX:0, tiltY:90, azimuth:Math.PI/2, altitude:0},
+ {tiltX:0, tiltY:-90, azimuth:3*Math.PI/2, altitude:0},
+ {tiltX:90,tiltY:0, azimuth:0, altitude:0},
+ {tiltX:90, tiltY:90, azimuth:0, altitude:0},
+ {tiltX:90, tiltY:-90, azimuth:0, altitude:0},
+ {tiltX:-90,tiltY:0, azimuth:Math.PI, altitude:0},
+ {tiltX:-90, tiltY:90, azimuth:0, altitude:0},
+ {tiltX:-90, tiltY:-90, azimuth:0, altitude:0},
+ {tiltX:0, tiltY:45, azimuth:Math.PI/2, altitude:Math.PI/4},
+ {tiltX:0, tiltY:-45, azimuth:3*Math.PI/2, altitude:Math.PI/4},
+ {tiltX:45, tiltY:0, azimuth:0, altitude:Math.PI/4},
+ {tiltX:-45, tiltY:0, azimuth:Math.PI, altitude:Math.PI/4},].forEach(
+ (el)=>{
+ test(function(){
+ var event = new PointerEvent("pointerdown",{pointerId:"pointer1", tiltX:el.tiltX, tiltY:el.tiltY});
+ assert_equals(event.azimuthAngle, el.azimuth, "azimuth angle should be " + el.azimuth);
+ assert_equals(event.altitudeAngle, el.altitude, "altitude angle should be " + el.altitude);
+ }, `tiltX,tiltY to azimuth/altitude when tiltX=${el.tiltX} and tiltY=${el.tiltY}`);
+ });
+
+ test(function(){
+ var event = new PointerEvent("pointerdown",{});
+ assert_equals(event.azimuthAngle, 0, "azimuth angle should be 0");
+ assert_equals(event.altitudeAngle, Math.PI/2, "altitude angle should be " + Math.PI/2);
+ }, "tiltX/tiltY to azimuth/altitude when tiltX/tiltY are not populated");
+
+ [{azimuth:0, altitude:0, tiltX:90,tiltY:0},
+ {azimuth:0, altitude:Math.PI/4, tiltX:45, tiltY:0},
+ {azimuth:0, altitude:Math.PI/2, tiltX:0,tiltY:0},
+ {azimuth:Math.PI/2, altitude:0, tiltX:0, tiltY:90},
+ {azimuth:Math.PI/2, altitude:Math.PI/4, tiltX:0, tiltY:45},
+ {azimuth:Math.PI, altitude:0, tiltX:-90,tiltY:0},
+ {azimuth:Math.PI, altitude:Math.PI/4, tiltX:-45, tiltY:0},
+ {azimuth:3*Math.PI/2, altitude:0, tiltX:0, tiltY:-90},
+ {azimuth:3*Math.PI/2, altitude:Math.PI/4, tiltX:0, tiltY:-45},].forEach(
+ (el)=>{
+ test(function(){
+ var event = new PointerEvent("pointerdown",{pointerId:"pointer1", azimuthAngle:el.azimuth, altitudeAngle:el.altitude});
+ assert_equals(event.tiltX, el.tiltX, "tiltX angle should be " + el.tiltX);
+ assert_equals(event.tiltY, el.tiltY, "tiltY angle should be " + el.tiltY);
+ }, `azimuth/altitude to tiltX/tiltY when azimuth=${el.azimuth} and altitude=${el.altitude}`);
+ });
+
+ test(function(){
+ var event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltX:45});
+ assert_equals(event.tiltX, 45, "tiltX value should stay as initialized");
+ assert_equals(event.tiltY, 0, "tiltY value should be initialized with default value 0");
+ assert_equals(event.azimuthAngle, 0, `for (tiltX, tiltY) = (${event.tiltX}, ${event.tiltY}) azimuthAngle should be 0`);
+ assert_equals(event.altitudeAngle, Math.PI/4, `for (tiltX, tiltY) = (${event.tiltX}, ${event.tiltY}) altitudeAngle should be PI/4`);
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltY:45});
+ assert_equals(event.tiltX, 0, "tiltX value should be initialized with default value 0");
+ assert_equals(event.tiltY, 45, "tiltY value should stay as initialized");
+ assert_equals(event.azimuthAngle, Math.PI/2, `for (tiltX, tiltY) = (${event.tiltX}, ${event.tiltY}) azimuthAngle should be PI/2`);
+ assert_equals(event.altitudeAngle, Math.PI/4, `for (tiltX, tiltY) = (${event.tiltX}, ${event.tiltY}) altitudeAngle should be PI/4`);
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", azimuthAngle:Math.PI/4});
+ assert_equals(event.azimuthAngle, Math.PI/4, "azimuthAngle value should stay as initialized");
+ assert_equals(event.altitudeAngle, Math.PI/2, "altitudeAngle should be initialized with default value of PI/2");
+ assert_equals(event.tiltX, 0, `for (azimuthAngle, altitudeAngle)=(${event.azimuthAngle},${event.altitudeAngle}) tiltX angle should be 0`);
+ assert_equals(event.tiltY, 0, `for (azimuthAngle, altitudeAngle)=(${event.azimuthAngle},${event.altitudeAngle}) tiltY angle should be 0`);
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", altitudeAngle:Math.PI/4});
+ assert_equals(event.azimuthAngle, 0, "azimuthAngle value should be initialized with default value of 0");
+ assert_equals(event.altitudeAngle, Math.PI/4, "altitudeAngle should stay as initialized");
+ assert_equals(event.tiltX, 45, `for (azimuthAngle, altitudeAngle)=(${event.azimuthAngle},${event.altitudeAngle}) tiltX angle should be 45 degrees`);
+ assert_equals(event.tiltY, 0, `for (azimuthAngle, altitudeAngle)=(${event.azimuthAngle},${event.altitudeAngle}) tiltY angle should be 0`);
+ }, "If only one of the values (tiltX, tiltY) or (azimuthAngle, altitudeAngle) is available the other one is set to the default value");
+
+ test(function(){
+ var event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltX:45, azimuthAngle:Math.PI/4});
+ assert_equals(event.tiltX, 45, "tiltX value should stay as initialized");
+ assert_equals(event.tiltY, 0, "tiltY value should be initialized with default value 0");
+ assert_equals(event.azimuthAngle, Math.PI/4, "azimuthAngle should stay as initialized");
+ assert_equals(event.altitudeAngle, Math.PI/2, `altitudeAngle value should be initialized with default value ${Math.PI/2}`);
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltY:45, azimuthAngle:Math.PI/4});
+ assert_equals(event.tiltX, 0, "tiltX value should be initialized with default value 0");
+ assert_equals(event.tiltY, 45, "tiltY value should stay as initialized");
+ assert_equals(event.azimuthAngle, Math.PI/4, "azimuthAngle value should stay as initialized");
+ assert_equals(event.altitudeAngle, Math.PI/2, `altitudeAngle value should be initialized with default value ${Math.PI/2}`);
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltX:45, altitudeAngle:Math.PI/4});
+ assert_equals(event.tiltX, 45, "tiltX value should stay as initialized");
+ assert_equals(event.tiltY, 0, "tiltY value should be initialized with default value 0");
+ assert_equals(event.azimuthAngle, 0, "azimuthAngle value should be initialized with default value of 0");
+ assert_equals(event.altitudeAngle, Math.PI/4, "altitudeAngle should stay as initialized");
+
+ event = new PointerEvent("pointerdown", {pointerId:"pointer1", tiltY:45, altitudeAngle:Math.PI/4});
+ assert_equals(event.tiltX, 0, "tiltX value should be initialized with default value 0");
+ assert_equals(event.tiltY, 45, "tiltY value should stay as initialized");
+ assert_equals(event.azimuthAngle, 0, "azimuthAngle value should be initialized with default value of 0");
+ assert_equals(event.altitudeAngle, Math.PI/4, "altitudeAngle should stay as initialized");
+ }, "If one of the values in both sets is provided, the other value in the set is initialized with the default value")
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-auto-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-auto-css_touch.html
new file mode 100644
index 0000000000..c486d77c11
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-auto-css_touch.html
@@ -0,0 +1,141 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: auto</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: auto;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+ var actions_promise;
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(event.pointerType, "touch", "wrong pointer type was detected: ");
+ });
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ }
+ });
+
+ // Inject touch inputs and wait for all the actions finish to end the test.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ });
+ }
+ </script>
+ <h1>touch-action: auto</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-button-none-test_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-button-none-test_touch.html
new file mode 100644
index 0000000000..42bcb7b843
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-button-none-test_touch.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Button touch-action test</title>
+ <meta name="assert" content="TA15.11 -The touch-action CSS property applies to button elements.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 100px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ button {
+ touch-action: none;
+ width: 350px;
+ height: 350px;
+ border: 2px solid red;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside of the "Test Button" element. Tap Complete button under the rectangle when done.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <button id="testButton">Test Button</button>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ //TA 15.11
+ var test_touchaction = async_test("touch-action attribute test in element");
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "button scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "button scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(testButton, 'down').then(function() {
+ return touchScrollInTarget(testButton, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-illegal.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-illegal.html
new file mode 100644
index 0000000000..5fe6179840
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-illegal.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: illegal</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 50px;
+ touch-action: pan-x none;
+ }
+ #target1 {
+ width: 700px;
+ height: 50px;
+ background: black;
+ margin-top: 5px;
+ touch-action: pan-y none;
+ }
+ #target2 {
+ width: 700px;
+ height: 50px;
+ background: black;
+ margin-top: 5px;
+ touch-action: auto none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Test will automatically check behaviour of following combinations: 'pan-x none', 'pan-y none', 'auto none'</h4>
+ <div id="target0"></div>
+ <div id="target1"></div>
+ <div id="target2"></div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ setup({ explicit_done: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById('target0');
+ var target1 = document.getElementById('target1');
+ var target2 = document.getElementById('target2');
+
+ test(function() {
+ assert_true(getComputedStyle(target0).touchAction == 'auto', "'pan-x none' is corrected properly");
+ }, "'pan-x none' is corrected properly");
+ test(function() {
+ assert_true(getComputedStyle(target1).touchAction == 'auto', "'pan-y none' is corrected properly");
+ }, "'pan-y none' is corrected properly");
+ test(function() {
+ assert_true(getComputedStyle(target2).touchAction == 'auto', "'auto none' is corrected properly");
+ }, "'auto none' is corrected properly");
+ done();
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html
new file mode 100644
index 0000000000..53eafbb2f2
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: auto > child: none</title>
+ <meta name="assert" content="TA15.5 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `auto`, Grand-Child: `none`">
+ <meta name="viewport" content="width=device-width">
+ <meta name="timeout" content="long">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: auto;
+ }
+ .scroller > div div {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if touch-action attribute works properly for embedded divs
+ // Scrollable-Parent, Child: `auto`, Grand-Child: `none`
+ // TA: 15.5
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(scrollTarget, 'down').then(function() {
+ return touchScrollInTarget(scrollTarget, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html
new file mode 100644
index 0000000000..dc234cbef3
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: child: none</title>
+ <meta name="assert" content="TA15.9 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `none`">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.9
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(scrollTarget, 'down').then(function() {
+ return touchScrollInTarget(scrollTarget, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html
new file mode 100644
index 0000000000..a820b4f3cf
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html
@@ -0,0 +1,128 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x > child: pan-x</title>
+ <meta name="timeout" content="long">
+ <meta name="assert" content="TA15.6 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `pan-x`, Grand-Child: `pan-x`">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x;
+ }
+ .scroller > div div {
+ touch-action: pan-x;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.6
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "scroll x offset should not be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(scrollTarget, 'down').then(function() {
+ return touchScrollInTarget(scrollTarget, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>behaviour: pan-x</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html
new file mode 100644
index 0000000000..99677313de
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x > child: pan-y</title>
+ <meta name="assert" content="TA15.13 - Touch action inherits child 'pan-x' -> child 'pan-y' test">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x;
+ }
+ .scroller > div div {
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if touch-action attribute works properly for embedded divs
+ // Scrollable-Parent, Child: `pan-x`, Grand-Child: `pan-y`
+ // TA: 15.13
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(scrollTarget, 'down').then(function() {
+ return touchScrollInTarget(scrollTarget, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch.html
new file mode 100644
index 0000000000..3ec5998b54
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch.html
@@ -0,0 +1,145 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent: none + two embedded children</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #divParent {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN inside blue rectangle. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="divParent">
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+ add_completion_callback(enableScrolling);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+ var actions_promise;
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ yScrollIsReceived = true;
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ }
+ });
+
+ // Inject touch inputs and wait for all the actions finish to end the test.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ });
+ }
+
+ function enableScrolling() {
+ document.getElementById('divParent').setAttribute('style', 'touch-action: auto');
+ }
+ </script>
+ <h1>behaviour: auto</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html
new file mode 100644
index 0000000000..d927ed94b3
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: inherit from parent: none</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller {
+ touch-action: none;
+ }
+ #scrollTarget {
+ margin: 10px;
+ width: 125vh;
+ height: 125vh;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Pointer Events: effective touch-action is "none" when parent has "none"
+ and target has "auto"</h1>
+ <h4 id="desc">
+ Try to scroll DOWN then RIGHT from inside blue rectangle.
+ Tap Complete button under the rectangle when done.
+ Expectation: no panning
+ </h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ Try to scroll DOWN then RIGHT from here.
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <h1>behavior: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected:
+ <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script type='text/javascript'>
+ window.onload = async () => {
+ const target0 = document.getElementById("target0");
+ const btnComplete = document.getElementById("btnComplete");
+
+ assert_equals(getComputedStyle(target0).touchAction,'none',
+ 'Expect touch-action:none');
+
+ let pointerup_received = false;
+ let pointercancel_received = false;
+ let scroll_received = false;
+
+ target0.addEventListener("pointerup",
+ () => pointerup_received = true);
+ target0.addEventListener("pointercancel",
+ () => pointercancel_received = true);
+ target0.addEventListener("scroll",
+ () => scroll_received = true);
+
+ let detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ promise_test(async () => {
+ const button_complete_click = getEvent("click", btnComplete);
+
+ await touchScrollInTarget(scrollTarget, 'down');
+ await touchScrollInTarget(scrollTarget, 'right');
+ await clickInTarget("touch", btnComplete);
+ await button_complete_click;
+
+ assert_true(
+ pointerup_received,
+ "expected pointerup event with scroll gestures on " +
+ "touch-action:none");
+ assert_false(
+ pointercancel_received,
+ "unexpected pointercancel event with scroll gestures on " +
+ "touch-action:none");
+ assert_false(
+ scroll_received,
+ "unexpected scroll event with scroll gestures on " +
+ "touch-action:none");
+ assert_equals(target0.scrollLeft, 0,
+ "expected scrollLeft 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0,
+ "expected scrollTop 0 in the end of the test");
+
+ updateDescriptionComplete();
+ }, "touch-action attribute test");
+ };
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-keyboard.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-keyboard.html
new file mode 100644
index 0000000000..ba087ab641
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-keyboard.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: keyboard</title>
+ <meta name="timeout" content="long">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Press DOWN ARROW key. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for keyboard only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+ var actions_promise;
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ target0.focus();
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStepKeyboard();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ }
+ });
+
+ // Inject keyboard scroll inputs.
+ const arrow_down = "\uE015";
+ const arrow_right = "\uE014";
+
+ actions_promise = new test_driver.Actions()
+ .addPointer("mousePointer1", "mouse")
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .addTick()
+ .send();
+ actions_promise = actions_promise
+ .then(()=>test_driver.send_keys(target0, arrow_down))
+ .then(()=>test_driver.send_keys(target0, arrow_down))
+ .then(()=>test_driver.send_keys(target0, arrow_right))
+ .then(()=>test_driver.send_keys(target0, arrow_right));
+ }
+
+ function updateDescriptionNextStepKeyboard() {
+ document.getElementById('desc').innerHTML = "Test Description: press RIGHT ARROW key.";
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-modified_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-modified_touch.html
new file mode 100644
index 0000000000..a91022504a
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-modified_touch.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Deleting touch-action elem after pointerdown has no effect</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ div.box {
+ margin: 10px;
+ height: 25vh;
+ border: 1px solid black;
+ }
+ #target {
+ background-color: lightgreen;
+ touch-action:none;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Deleting touch-action elem after pointerdown has no effect</h1>
+ <h4 id="desc">
+ Try to scroll up/down starting at the green element. Expectation: the
+ green element would vanish immediately and the content won't scroll.
+ </h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box" id="target">Drag up or down from here</div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </body>
+ <script>
+ let target = document.getElementById("target");
+ let pointercancel_received = false;
+
+ document.body.addEventListener("pointerdown", () => target.parentElement.removeChild(target));
+ document.body.addEventListener("pointercancel", () => pointercancel_received = true);
+
+ let detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ promise_test(async () => {
+ let pointerup_event = getEvent("pointerup", document.body);
+
+ await touchScrollInTarget(target, "down");
+ await pointerup_event;
+
+ assert_false(pointercancel_received,
+ "a pointercancel event is unexpected because there should be no scrolling");
+
+ updateDescriptionComplete();
+ }, "Deleting touch-action elem after pointerdown");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-mouse.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-mouse.html
new file mode 100644
index 0000000000..9eb3839ec0
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-mouse.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: mouse</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text down using mouse (use mouse wheel or click on the scrollbar). Wait for description update.</h4>
+ <p>Note: this test is for mouse only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+ var actions_promise;
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStepMouse();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ }
+ });
+
+ const posOffsetX = 0;
+ const posOffsetY = 0;
+ const scrollDeltaX = 200;
+ const scrollDeltaY = 200;
+ actions_promise = new test_driver.Actions()
+ .scroll(posOffsetX, posOffsetY, scrollDeltaX, scrollDeltaY,
+ {origin: target0})
+ .send();
+ }
+
+ function updateDescriptionNextStepMouse() {
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll text right using mouse (use mouse wheel or click on the scrollbar).";
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-css_touch.html
new file mode 100644
index 0000000000..2ff161a915
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-css_touch.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: none</title>
+ <meta name="assert" content="TA15.2 - With `touch-action: none` on a swiped or click/dragged element, `pointerdown+(optional pointermove)+pointerup` must be dispatched.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if "touch-action: none" attribute works properly
+ //TA: 15.2
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-on-body-when-style-propagates-to-viewport_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-on-body-when-style-propagates-to-viewport_touch.html
new file mode 100644
index 0000000000..b2a3a9326e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-none-on-body-when-style-propagates-to-viewport_touch.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<title>Touch Action None on body when style propagates from body to viewport</title>
+<head>
+ <style>
+ html {
+ touch-action: none;
+ }
+ body {
+ overflow: auto;
+ }
+ </style>
+ <link rel="help" href="crbug.com/1031745">
+ <link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
+ <link rel="help" href="https://w3c.github.io/pointerevents/#determining-supported-touch-behavior">
+ <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="pointerevent_support.js"></script>
+</head>
+<body onload="onLoad()">
+ <h2>Pointer Events touch-action none on body when style propagates from body to viewport</h2>
+ <h4>Test Description: Try to touch scroll. You shouldn't be able to.</h4>
+ <p>Note: this test is for touch only</p>
+ Body with touch-action=none.
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <p>Paragraph with touch-action=none</p>
+ <script>
+ function onLoad(){
+ var body = document.body;
+
+ if(!window.promise_test)
+ return;
+
+ promise_test(function(t){
+ return new Promise(async function(resolve,reject){
+ await touchScrollInTarget(body, 'down');
+ await touchScrollInTarget(body, 'right');
+ t.step(function(){
+ assert_equals(document.scrollingElement.scrollLeft, 0, "scrollingElement scroll x offset should be 0 in the end of the test");
+ assert_equals(document.scrollingElement.scrollTop, 0, "scrollingElement scroll y offset should be 0 in the end of the test");
+ });
+ resolve();
+ }).then(
+ ()=>{t.done();},
+ ()=>{t.done();}
+ );}, "touch-action none on body when style propagates to viewport");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-down-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-down-css_touch.html
new file mode 100644
index 0000000000..804a5144f1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-down-css_touch.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-down</title>
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-down` on a swiped or click/dragged element, only panning in the y-axis down direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-down;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element UP (drag down), then RIGHT (drag left), then DOWN (drag up). Tap Complete button under the rectangle when done. Expected: only pans in down direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+ target0.scrollTop = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_greater_than_equal(target0.scrollTop, 200);
+ });
+
+ // Check if "touch-action: pan-down" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_greater_than(target0.scrollTop, 200, "scroll y offset should be greater than 200 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'up').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return touchScrollInTarget(target0, 'down');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-down</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-left-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-left-css_touch.html
new file mode 100644
index 0000000000..99c7ad8132
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-left-css_touch.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-left</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-left` on a swiped or click/dragged element, only panning on the x-axis left direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-left;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then RIGHT (drag left), then LEFT (drag right). Tap Complete button under the rectangle when done. Expected: only pans in left direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+ target0.scrollLeft = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_less_than_equal(target0.scrollLeft, 200);
+ });
+
+ // Check if "touch-action: pan-left" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_less_than(target0.scrollLeft, 200, "scroll x offset should be less than 200 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return touchScrollInTarget(target0, 'left');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-left</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-right-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-right-css_touch.html
new file mode 100644
index 0000000000..99882a079c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-right-css_touch.html
@@ -0,0 +1,132 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-right</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-right` on a swiped or click/dragged element, only panning on the x-axis right direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <meta name="timeout" content="long">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-right;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then LEFT (drag right), then RIGHT (drag left). Tap Complete button under the rectangle when done. Expected: only pans in right direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+ target0.scrollLeft = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_greater_than_equal(target0.scrollLeft, 200);
+ });
+
+ // Check if "touch-action: pan-right" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_greater_than(target0.scrollLeft, 200, "scroll x offset should be greater than 200 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'left');
+ }).then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-right</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-up-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-up-css_touch.html
new file mode 100644
index 0000000000..565d0d1d3e
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-up-css_touch.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-up</title>
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-up` on a swiped or click/dragged element, only panning in the y-axis up direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-up;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then RIGHT (drag left), then UP (drag down). Tap Complete button under the rectangle when done. Expected: only pans in up direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+ target0.scrollTop = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_less_than_equal(target0.scrollTop, 200);
+ });
+
+ // Check if "touch-action: pan-up" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_less_than(target0.scrollTop, 200, "scroll y offset should be less than 200 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return touchScrollInTarget(target0, 'up');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-up</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-css_touch.html
new file mode 100644
index 0000000000..d2b147da0a
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-css_touch.html
@@ -0,0 +1,121 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-x</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-x` on a swiped or click/dragged element, only panning on the x-axis should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-x;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if "touch-action: pan-x" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "scroll x offset should not be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-x</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html
new file mode 100644
index 0000000000..e081cacbe4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x pan-y > child: pan-y</title>
+ <meta name="assert" content="TA15.17 - Touch action 'pan-x pan-y' 'pan-y' test">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x pan-y;
+ }
+ .scroller > div div {
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.17
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "scroll y offset should not be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>behaviour: pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch.html
new file mode 100644
index 0000000000..b80035c98d
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch.html
@@ -0,0 +1,138 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-x pan-y</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-x pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+ var actions_promise;
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ }
+ });
+
+ // Inject touch inputs and wait for all the actions finish to end the test.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ });
+ }
+ </script>
+ <h1>touch-action: pan-x pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-y-css_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-y-css_touch.html
new file mode 100644
index 0000000000..adf502f1d8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-pan-y-css_touch.html
@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-y</title>
+ <meta name="timeout" content="long">
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-y` on a swiped or click/dragged element, only panning in the y-axis should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ // Check if "touch-action: pan-y" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "scroll y offset should not be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(target0, 'down').then(function() {
+ return touchScrollInTarget(target0, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html
new file mode 100644
index 0000000000..194e933f91
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: touch-action in rotated divs</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <link rel="help" href="https://w3c.github.io/pointerevents/#declaring-candidate-regions-for-default-touch-behaviors" />
+ <meta name="assert" content="Tests that touch-action directions in a div rotated by 90-degrees are interpreted in the local (rotated) coordinate space"/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var event_log = [];
+
+ function resetTestState() {
+ event_log = [];
+ }
+
+ function run() {
+ var current_test = 0;
+ var test_params = [
+ {
+ test_obj: setup_pointerevent_test("'touch-action: pan-x' in a rotated div", ["touch"]),
+ touch_action: "pan-x",
+ expected_events: "pointercancel, pointercancel, pointerup, pointerup"
+ },
+ {
+ test_obj: setup_pointerevent_test("'touch-action: pan-y' in a rotated div", ["touch"]),
+ touch_action: "pan-y",
+ expected_events: "pointerup, pointerup, pointercancel, pointercancel"
+ },
+ ];
+
+ function setCurrentTouchAction() {
+ document.getElementById("target").style.touchAction = test_params[current_test].touch_action;
+ }
+
+ setCurrentTouchAction();
+
+ on_event(document.getElementById("btnDone"), "click", function() {
+ test_params[current_test].test_obj.step(function () {
+ assert_equals(event_log.join(", "), test_params[current_test].expected_events);
+ });
+
+ event_log = [];
+
+ test_params[current_test++].test_obj.done();
+ if (current_test < 2)
+ setCurrentTouchAction();
+ });
+
+ ["pointerup", "pointercancel"].forEach(function(eventName) {
+ on_event(document.getElementById("target"), eventName, function (event) {
+ event_log.push(event.type);
+ });
+ });
+ }
+ </script>
+ <style>
+ #target {
+ width: 200px;
+ height: 200px;
+ margin: 10px;
+ float: left;
+ touch-action: none;
+ background-color: green;
+ transform: rotate(-90deg);
+ }
+
+ #btnDone {
+ padding: 20px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: touch-action in rotated divs</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Tests that touch-action directions in a div rotated by 90-degrees are interpreted in the local (rotated) coordinate space
+ </h4>
+ <ol>
+ <li>Make 4 separate touch drags on Green, in this order: drag UP, then drag DOWN, then drag LEFT, then drag RIGHT.</li>
+ <li>Tap on Done.</li>
+ <li>Repeat the touch drags once again, in the same order.</li>
+ <li>Tap on Done.</li>
+ </ol>
+ <div id="target"></div>
+ <input type="button" id="btnDone" value="Done" />
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-span-none-test_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-span-none-test_touch.html
new file mode 100644
index 0000000000..845da068e6
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-span-none-test_touch.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Span touch-action test</title>
+ <meta name="timeout" content="long">
+ <meta name="assert" content="TA15.18 - The touch-action CSS property applies to all elements except non-replaced inline elements.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 150px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ #testspan {
+ touch-action: none;
+ font-size: 72pt;
+ padding: 0px 0px 180px 0px;
+ border: 2px solid red;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the span element. Tap Complete button under the rectangle when done.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <span id="testspan">
+ Test span
+ </span>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ //TA 15.18
+ var test_touchaction = async_test("touch-action attribute test in element");
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "span scroll x offset should not be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "span scroll y offset should not be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(testspan, 'down').then(function() {
+ return touchScrollInTarget(testspan, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-svg-none-test_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-svg-none-test_touch.html
new file mode 100644
index 0000000000..252ed2fed0
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-svg-none-test_touch.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>SVG test</title>
+ <meta name="timeout" content="long">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 350px;
+ width: 300px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the circle. Tap Complete button under the rectangle when done.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <svg id="testSvg" width="555" height="555" style="touch-action: none; border: 4px double red;">
+ <circle cx="305" cy="305" r="250" stroke="green" stroke-width="4" fill="yellow" />
+ Sorry, your browser does not support inline SVG.
+ </svg>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ var test_touchaction = async_test("touch-action attribute test in SVG");
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "SVG scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "SVG scroll y offset should be 0 in the end of the test");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(testSvg, 'down').then(function() {
+ return touchScrollInTarget(testSvg, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-table-none-test_touch.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-table-none-test_touch.html
new file mode 100644
index 0000000000..6f5e16a8c5
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-table-none-test_touch.html
@@ -0,0 +1,139 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Table touch-action test</title>
+ <meta name="timeout" content="long">
+ <meta name="assert" content="TA15.19 The touch-action CSS property applies to all elements except table rows, row groups, table columns, and column groups.">
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 150px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ #testtable{
+ color: white;
+ width: 350px;
+ padding: 0px 0px 200px 0px;
+ border: 2px solid green;
+ }
+ .testtd, .testth {
+ border: 2px solid green;
+ height: 80px;
+ }
+ #row1 {
+ touch-action: none;
+ }
+ #cell3 {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT starting your touch over the 1st Row. <br>
+ And try to scroll element DOWN then RIGHT starting your touch inside of the Cell 3, then tap complete button.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <table id="testtable">
+ <caption>The caption, first row element, and cell 3 have touch-action: none.</caption>
+ <tr id="row1"><th class="testth">Header 1 <td class="testtd">Cell 1 <td class="testtd">Cell 2</tr>
+ <tr id="row2"><th class="testth">Header 2 <td id="cell3" class="testtd">Cell 3 <td class="testtd">Cell 4</tr>
+ <tr id="row3"> <th class="testth">Header 3 <td class="testtd">Cell 5 <td class="testtd"> Cell 6</tr>
+ </table>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+ var isFirstPart = true;
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ var actions_promise;
+
+ //TA 15.19
+ var test_touchaction_cell = async_test("touch-action attribute test on the cell");
+ var test_touchaction_row = async_test("touch-action attribute test on the row");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction_cell.step(function() {
+ assert_equals(target0.scrollLeft, xScr1, "table scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, yScr1, "table scroll y offset should be 0 in the end of the test");
+ assert_true(xScrollIsReceived && yScrollIsReceived, "target0 x and y scroll offsets should be greater than 0 after first two interactions (outside red border) respectively");
+ });
+
+ // Make sure the test finishes after all the input actions are completed.
+ actions_promise.then( () => {
+ test_touchaction_cell.done();
+ });
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ if(isFirstPart) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction_row.step(function () {
+ yScrollIsReceived = true;
+ });
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction_row.done();
+ }
+ }
+ else {
+ test_touchaction_cell.step(failOnScroll, "scroll received while shouldn't");
+ }
+ });
+
+ // Inject touch inputs.
+ actions_promise = touchScrollInTarget(row1, 'down').then(function() {
+ return touchScrollInTarget(row1, 'right');
+ }).then(function() {
+ isFirstPart = false;
+ return touchScrollInTarget(cell3, 'down');
+ }).then(function() {
+ return touchScrollInTarget(cell3, 'right');
+ }).then(function() {
+ return clickInTarget("touch", btnComplete);
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-action-verification.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-verification.html
new file mode 100644
index 0000000000..6a47eaee27
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-action-verification.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: basic verification</title>
+ <meta name="assert" content="TA15.20 - The touch-action CSS property determines whether touch input MAY trigger default behavior supplied by the user agent.
+ auto: The user agent MAY determine any permitted touch behaviors, such as panning and zooming manipulations of the viewport, for touches that begin on the element.
+ none: Touches that begin on the element MUST NOT trigger default touch behaviors.
+ pan-x: The user agent MAY consider touches that begin on the element only for the purposes of horizontally scrolling the element's nearest ancestor with horizontally scrollable content.
+ pan-y: The user agent MAY consider touches that begin on the element only for the purposes of vertically scrolling the element's nearest ancestor with vertically scrollable content.
+ manipulation: The user agent MAY consider touches that begin on the element only for the purposes of scrolling and continuous zooming. Any additional behaviors supported by auto are out of scope for this specification.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ /*
+ Give some rules below something to override in order to test
+ that they really are being parsed
+ */
+ .defnone {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Test will automatically check parsing behaviour of various touch-action combinations.</h4>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ setup({ explicit_done: true });
+
+ function run() {
+ var tests = document.querySelectorAll('.test');
+ //TA 15.20
+ for (var i = 0; i < tests.length; i++) {
+ test(function() {
+ var style = window.getComputedStyle(tests[i]);
+ assert_equals(tests[i].attributes.expected.value, style.touchAction);
+ }, tests[i].id);
+ }
+ done();
+ }
+ </script>
+ <h1>touch-action: basic verification</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ <div class="test" id="default" expected="auto"></div>
+ <div class="test defnone" id="stylesheet-none" expected="none"></div>
+ <div class="test defnone" id="explicit-auto" style="touch-action: auto;" expected="auto"></div>
+ <div class="test" id="explicit-pan-x" style="touch-action: pan-x;" expected="pan-x"></div>
+ <div class="test" id="explicit-pan-left" style="touch-action: pan-left;" expected="pan-left"></div>
+ <div class="test" id="explicit-pan-right" style="touch-action: pan-right;" expected="pan-right"></div>
+ <div class="test" id="explicit-pan-y" style="touch-action: pan-y;" expected="pan-y"></div>
+ <div class="test" id="explicit-pan-up" style="touch-action: pan-up;" expected="pan-up"></div>
+ <div class="test" id="explicit-pan-down" style="touch-action: pan-down;" expected="pan-down"></div>
+ <div class="test" id="explicit-pan-x-pan-y" style="touch-action: pan-x pan-y;" expected="pan-x pan-y"></div>
+ <div class="test" id="explicit-pan-y-pan-x" style="touch-action: pan-y pan-x;" expected="pan-x pan-y"></div>
+ <div class="test" id="explicit-pan-left-pan-up" style="touch-action: pan-left pan-up;" expected="pan-left pan-up"></div>
+ <div class="test" id="explicit-pan-left-pan-down" style="touch-action: pan-left pan-down;" expected="pan-left pan-down"></div>
+ <div class="test" id="explicit-pan-right-pan-up" style="touch-action: pan-right pan-up;" expected="pan-right pan-up"></div>
+ <div class="test" id="explicit-pan-right-pan-down" style="touch-action: pan-right pan-down;" expected="pan-right pan-down"></div>
+ <div class="test" id="explicit-pan-up-pan-left" style="touch-action: pan-up pan-left;" expected="pan-left pan-up"></div>
+ <div class="test" id="explicit-pan-up-pan-right" style="touch-action: pan-up pan-right;" expected="pan-right pan-up"></div>
+ <div class="test" id="explicit-pan-down-pan-left" style="touch-action: pan-down pan-left;" expected="pan-left pan-down"></div>
+ <div class="test" id="explicit-pan-down-pan-right" style="touch-action: pan-down pan-right;" expected="pan-right pan-down"></div>
+ <div class="test" id="explicit-manipulation" style="touch-action: manipulation;" expected="manipulation"></div>
+ <div class="test" id="explicit-none" style="touch-action: none;" expected="none"></div>
+ <div class="test" id="explicit-invalid-1" style="touch-action: bogus;" expected="auto"></div>
+ <div class="test defnone" id="explicit-invalid-2" style="touch-action: auto pan-x;" expected="none"></div>
+ <div class="test" id="explicit-invalid-3" style="touch-action: pan-y none;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-4" style="touch-action: pan-x pan-x;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-5" style="touch-action: manipulation pan-x;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-6" style="touch-action: pan-x pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-7" style="touch-action: auto pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-8" style="touch-action: none pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-9" style="touch-action: pan-x pan-right;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-10" style="touch-action: pan-y pan-up;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-11" style="touch-action: pan-y pan-down;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-12" style="touch-action: pan-left pan-right;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-13" style="touch-action: pan-up pan-down;" expected="auto"></div>
+ <div style="touch-action: none;">
+ <div class="test" id="not-inherited" expected="auto"></div>
+ <div class="test" id="inherit" style="touch-action: inherit;" expected="none"></div>
+ </div>
+ <div class="test defnone" id="initial" style="touch-action: initial;" expected="auto"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_touch-adjustment_click_target.html b/testing/web-platform/tests/pointerevents/pointerevent_touch-adjustment_click_target.html
new file mode 100644
index 0000000000..46d5ac1793
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerevent_touch-adjustment_click_target.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Touch-generated events should have the same target</title>
+<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>
+<p>Touch letter 'O' below to run the test. If a "PASS" result appears the test passes, otherwise it fails</p>
+<p><a href="#" id="link">Link</a> <span id="target">O</span></p>
+<div id="log"></div>
+<script>
+let input_gesture;
+const target = document.getElementById('target');
+const xPosition = Math.ceil(target.offsetLeft + 2);
+const yPosition = Math.ceil(target.offsetTop + 2);
+
+function inject_input() {
+ let actions = new test_driver.Actions();
+ return actions
+ .addPointer("touchPointer", "touch")
+ .setPointer("touchPointer")
+ .pointerMove(xPosition, yPosition)
+ .pointerDown()
+ .pointerUp()
+ .send();
+}
+
+
+addEventListener('load', () => {
+ input_gesture = inject_input();
+});
+
+async_test(t => {
+ const link = document.getElementById('link');
+ const expectedEventLog = ['pointerdown-link', 'touchstart-link', 'pointerup-link', 'touchend-link', 'click-link'];
+ const eventLogRecorder = [];
+
+ const eventNames = ['touchstart', 'touchmove', 'touchend', 'pointerdown', 'pointermove', 'pointerup', 'click'];
+ for (eventName of eventNames) {
+ document.addEventListener(eventName, t.step_func(event => {
+ // TouchEvent and PointerEvent should have the same un-adjusted coordinates.
+ // click event should have coordinates adjusted to link element.
+ const eventClientX = event.clientX || (event.touches.length > 0 ? event.touches[0].clientX : 0);
+ const eventClientY = event.clientY || (event.touches.length > 0 ? event.touches[0].clientY : 0);
+
+ if (event.type === 'click') {
+ assert_equals(document.elementFromPoint(eventClientX, eventClientY), link,
+ 'click should have clientX/Y adjusted to link.');
+ } else if (event.type != 'touchend') {
+ assert_equals(eventClientX, xPosition,
+ `${event.type} should have un-adjusted x coordinates.`);
+ assert_equals(eventClientY, yPosition,
+ `${event.type} should have un-adjusted y coordinates.`);
+ }
+
+ // All events should have target adjusted to link.
+ const targetName = event.target.id || event.target.nodeName || '[null]';
+ eventLogRecorder.push(`${event.type}-${targetName}`);
+ if (event.type === 'click') {
+ assert_array_equals(eventLogRecorder, expectedEventLog);
+ input_gesture.then(() => { t.done(); });
+ }
+ }));
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html
new file mode 100644
index 0000000000..a793272522
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html
@@ -0,0 +1,107 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock tests</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <style>
+ #testContainer {
+ touch-action: none;
+ user-select: none;
+ position: relative;
+ }
+ </style>
+ <script>
+ var last_pointer_client_pos = {'x': 0, 'y': 0}
+ var last_pointer_screen_pos = {'x': 0, 'y': 0}
+
+ const kStateInit = 0
+ const kStateLocked = 1
+ const kStateUnlocked = 2.
+
+ var test_state = kStateInit;
+
+ function resetTestState() {
+ test_state = kStateInit;
+ document.exitPointerLock();
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("Test pointerevent coordinates when pointer is locked", ['mouse']);
+ var div1 = document.getElementById("target");
+
+ on_event(div1, 'click', function(event) {
+ if (test_state == kStateInit)
+ div1.requestPointerLock();
+ else if (test_state == kStateLocked)
+ document.exitPointerLock();
+ });
+ on_event(div1, 'pointermove', function(event) {
+ if (test_state == kStateLocked) {
+ test_pointerEvent.step(function() {
+ assert_equals(event.clientX, last_pointer_client_pos['x'], 'clientX')
+ assert_equals(event.clientY, last_pointer_client_pos['y'], 'clientY')
+ assert_equals(event.screenX, last_pointer_screen_pos['x'], 'screenX')
+ assert_equals(event.screenY, last_pointer_screen_pos['y'], 'screenY')
+ });
+ } else {
+ last_pointer_client_pos = {'x': event.clientX, 'y': event.clientY}
+ last_pointer_screen_pos = {'x': event.screenX, 'y': event.screenY}
+ }
+ });
+ on_event(document, 'pointerlockchange', function(event) {
+ if (test_state == kStateInit) {
+ test_state = kStateLocked;
+ test_pointerEvent.step(function() {
+ assert_equals(document.pointerLockElement, div1, "document.pointerLockElement should be div1.");
+ });
+
+ var actions = new test_driver.Actions();
+ pos_x = div1.offsetWidth / 2;
+ pos_y = div1.offsetHeight / 2;
+ for (var i = 0; i < 10; i++) {
+ // Move left and up.
+ pos_x += 10;
+ pos_y -= 10;
+ actions.pointerMove(pos_x, pos_y, {origin: div1});
+ }
+ actions.pointerDown().pointerUp().send();
+ } else if (test_state == kStateLocked) {
+ test_state = kStateUnlocked;
+ test_pointerEvent.step(function() {
+ assert_equals(document.pointerLockElement, null, "document.pointerLockElement should be null.");
+ });
+ test_pointerEvent.done();
+ }
+ });
+
+ new test_driver.Actions().pointerMove(/* x = */ 0, /* y = */ 0, {origin: div1}).pointerDown().pointerUp().send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events movement in locked state test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks the pointer event coordinates stays unchanged when pointer is locked.
+ <ol>
+ <li>Click left mouse button on the green rectangle.</li>
+ <li>Move the mouse around.</li>
+ <li>Click left mouse button again. </li>
+ </ol>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div id="target" style="width:200px;height:200px;background:green"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.https.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.https.html
new file mode 100644
index 0000000000..1aa381b466
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.https.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock tests</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <style>
+ #testContainer {
+ touch-action: none;
+ user-select: none;
+ position: relative;
+ }
+ </style>
+ <script>
+ var lock_change_count = 0;
+ var mouseeventMovements = []
+ var pointereventMovements = []
+ var has_coalesced_Events = false;
+
+ function resetTestState() {
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointermove getCoalescedEvents when lock test", ['mouse']);
+ var div1 = document.getElementById("target");
+
+ on_event(div1, 'pointerdown', function(event) {
+ div1.requestPointerLock();
+ });
+ on_event(div1, 'pointermove', function(event) {
+ if (document.pointerLockElement == div1) {
+ test_pointerEvent.step(function() {
+ assert_greater_than(event.getCoalescedEvents().length, 0, "document.pointerLockElement should have coalesced events.");
+ document.exitPointerLock();
+ has_coalesced_Events = true;;
+ });
+ }
+ });
+
+ // Inject mouse inputs.
+ pointerDragInTarget('mouse', div1, 'right').then(function() {
+ test_pointerEvent.step(function () {
+ assert_true(has_coalesced_Events, "document.pointerLockElement should have coalesced events.");
+ }, "document.pointerLockElement should have coalesced events.");
+ test_pointerEvent.done();
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>PointerMove getCoalescedEvent in locked state test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks if pointerevent.getCoalescedEvent work correctly when pointer is locked.
+ <ol>
+ <li>Press left button down on the green rectangle to lock pointer.</li>
+ <li>Move the mouse</li>
+ </ol>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div id="target" style="width:800px;height:250px;background:green"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getPredictedEvents_when_pointerlocked-manual.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getPredictedEvents_when_pointerlocked-manual.html
new file mode 100644
index 0000000000..aba08f14b8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_getPredictedEvents_when_pointerlocked-manual.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <style>
+ #testContainer {
+ touch-action: none;
+ user-select: none;
+ position: relative;
+ }
+ </style>
+ <script>
+ var lock_change_count = 0;
+ var move_event_count = 0;
+ var mouseeventMovements = []
+ var pointereventMovements = []
+
+ function resetTestState() {
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointermove getPredictedEvents when lock test", ['mouse']);
+ var div1 = document.getElementById("target");
+
+ on_event(div1, 'pointerdown', function(event) {
+ div1.requestPointerLock();
+ });
+ on_event(div1, 'pointermove', function(event) {
+ if (document.pointerLockElement == div1) {
+ test_pointerEvent.step(function() {
+ if (event.getPredictedEvents().length > 0) {
+ for (var i=0; i<event.getPredictedEvents().length; i++) {
+ var predictedEvent = event.getPredictedEvents()[i];
+ test_pointerEvent.step(function() {
+ assert_equals(predictedEvent.pointerId, event.pointerId, "getPredictedEvents()[" + i + "].pointerId");
+ assert_equals(predictedEvent.pointerType, event.pointerType, "getPredictedEvents()[" + i + "].pointerType");
+ assert_equals(predictedEvent.target, document.pointerLockElement, "getPredictedEvents()[" + i + "].target");
+ assert_equals(predictedEvent.clientX, event.clientX, "getPredictedEvents()[" + i + "].clientX");
+ assert_equals(predictedEvent.clientY, event.clientY, "getPredictedEvents()[" + i + "].clientY");
+ });
+ }
+ document.exitPointerLock();
+ test_pointerEvent.done();
+ } else {
+ assert_less_than(++move_event_count, 20, "pointermove have no predicted event in 20 moves")
+ }
+ });
+ }
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>PointerMove getPredictedEvent in locked state test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks if pointerevent.getPredictedEvent work correctly when pointer is locked.
+ <ol>
+ <li>Press left button down on the green rectangle to lock pointer.</li>
+ <li>Move the mouse</li>
+ </ol>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div id="target" style="width:800px;height:250px;background:green"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html
new file mode 100644
index 0000000000..00a2b85f59
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html
@@ -0,0 +1,123 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="/external/wpt/pointerevents/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>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <style>
+ #testContainer {
+ touch-action: none;
+ user-select: none;
+ position: relative;
+ }
+ </style>
+ <script>
+ PhaseEnum = {
+ BeforeLocked: 0,
+ PointerLocked: 1,
+ PointerUnlocked: 2,
+ Done: 3,
+ };
+ var phase = PhaseEnum.BeforeLocked;
+ var last_event;
+ var mouseeventMovements = []
+ var pointereventMovements = []
+
+ function resetTestState() {
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent movementX/Y with pointerlock test", ['mouse']);
+
+ function testPointerMoves(event) {
+ if (last_event) {
+ if (phase == PhaseEnum.PointerLocked){
+ test_pointerEvent.step(function() {
+ assert_equals(event.screenX, last_event.screenX);
+ assert_equals(event.screenY, last_event.screenY);
+ });
+ } else { // BeforeLocked || Unlocked
+ test_pointerEvent.step(function() {
+ assert_equals(event.screenX - last_event.screenX, event.movementX);
+ assert_equals(event.screenY - last_event.screenY, event.movementY);
+ });
+ }
+ }
+ last_event = event;
+ }
+
+ on_event(target, 'click', function(event) {
+ if (phase == PhaseEnum.BeforeLocked)
+ target.requestPointerLock();
+ else if (phase == PhaseEnum.PointerLocked)
+ document.exitPointerLock();
+ else if (phase == PhaseEnum.PointerUnlocked)
+ test_pointerEvent.done();
+ });
+ on_event(target, 'pointermove', function(event) {
+ if (phase == PhaseEnum.PointerLocked) {
+ pointereventMovements.push(`${event.movementX}, ${event.movementY}`);
+ }
+ testPointerMoves(event);
+ });
+ on_event(target, 'mousemove', function(event) {
+ if (phase == PhaseEnum.PointerLocked) {
+ mouseeventMovements.push(`${event.movementX}, ${event.movementY}`);
+ }
+ });
+ on_event(document, 'pointerlockchange', function(event) {
+ phase++;
+ last_event = null;
+ if (phase == PhaseEnum.PointerLocked) {
+ test_pointerEvent.step(function() {
+ assert_equals(document.pointerLockElement, target, "document.pointerLockElement should be target.");
+ });
+ } else if (phase == PhaseEnum.PointerUnlocked) {
+ test_pointerEvent.step(function() {
+ assert_equals(document.pointerLockElement, null, "document.pointerLockElement should be null.");
+ assert_not_equals(mouseeventMovements.length, 0);
+ assert_array_equals(pointereventMovements, mouseeventMovements, "pointermove should have movementX/Y same as mousemove");
+ });
+ }
+ pointerHoverInTarget('mouse', target, 'right').then(function() {
+ return clickInTarget("mouse", target);
+ });
+ });
+
+ // Inject mouse inputs.
+ pointerHoverInTarget('mouse', target, 'right').then(function() {
+ return clickInTarget("mouse", target);
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events movement with pointerlock test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks pointerevent movementX/Y value with pointerlock.
+ It checks whether movement X/Y matches event.screenX/Y - last_event.screenX/Y when pointer is not locked;
+ And if pointermove.movementX/Y matches mousemove.movementX/Y when pointer is locked.
+ <ol>
+ <li>Move the mouse inside the green rectangle.</li>
+ <li>Click left button on the green rectangle.(Enter pointerlock)</li>
+ <li>Move the mouse around.</li>
+ <li>Click left button again</li>
+ <li>Move the mouse inside the green rectangle.</li>
+ <li>Click left button again to end the test.</li>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div id="target" style="width:800px;height:250px;background:green"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html
new file mode 100644
index 0000000000..c66584a939
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock test</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script>
+ var got_capture = false;
+ var lost_capture = false;
+ var lock_requested = false;
+
+ function resetTestState() {
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("no pointercapture while pointerlock", ['mouse']);
+ var div1 = document.getElementById("div1");
+ var div2 = document.getElementById("div2");
+
+ on_event(div1, 'pointerdown', function(event) {
+ div2.setPointerCapture(event.pointerId);
+ });
+ on_event(document, 'contextmenu', function(event) {
+ event.preventDefault();
+ });
+ on_event(div2, 'pointermove', function(event) {
+ if (event.button == 2 && got_capture && !lock_requested) {
+ div1.requestPointerLock();
+ lock_requested = true;
+ }
+ });
+ on_event(div2, 'gotpointercapture', function(event) {
+ got_capture = true;
+ });
+ on_event(div2, 'lostpointercapture', function(event) {
+ lost_capture = true;
+ });
+ on_event(document,"pointerlockerror", function() {
+ assert_unreached("Pointer lock error");
+ })
+
+ injectInput().then(function(){
+ test_pointerEvent.step(function(){
+ assert_true(lost_capture, "Pointer capture was lost after got a pointer lock.");
+ assert_equals(document.pointerLockElement, div1, "document.pointerLockElement should be div1.");
+ });
+ test_pointerEvent.done();
+ })
+ }
+
+ // Inject mouse input
+ function injectInput() {
+ var actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: div1})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerMove(30, 30, {origin: div1})
+ .pointerMove(30, 0, {origin: div1})
+ .pointerDown({button: actions.ButtonType.RIGHT})
+ .pointerMove(60, 30, {origin: div1})
+ .pointerMove(20, 20, {origin: div1})
+ .pointerUp({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events pointer lock test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks that we release the exsiting pointer capture when any element in the page gets a pointer lock.
+ <ol>
+ <li>Press left button down on the green rectangle and hold it.</li>
+ <li>Move the mouse inside the green rectangle.</li>
+ <li>Click right button while keeping left button down</li>
+ <li>Keep moving the mouse inside the green rectangle.</li>
+ </ol>
+
+ Test passes if the pointer capture is released on the yellow rectangle when the green rectangle gets the pointer lock.
+ </h4>
+ <div id="testContainer">
+ <div id="div1" style="width:800px;height:250px;background:green"></div>
+ <div id="div2" style="width:800px;height:250px;background:yellow"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html
new file mode 100644
index 0000000000..f3f61cf0d0
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events pointer lock tests</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ <script>
+ var lock_change_count = 0;
+ var capture_count = 0;
+ var mouse_move_count = 0;
+
+ function resetTestState() {
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("no pointercapture while pointerlock", ['mouse']);
+ var div1 = document.getElementById("div1");
+ var div2 = document.getElementById("div2");
+
+ on_event(div1, 'pointerdown', function(event) {
+ div2.setPointerCapture(event.pointerId);
+ div1.requestPointerLock();
+ });
+ on_event(div1, 'pointermove', function(event) {
+ mouse_move_count++
+ if (lock_change_count == 1) {
+ if (mouse_move_count == 2) {
+ try {
+ div2.setPointerCapture(event.pointerId);
+ test_pointerEvent.step(function () {
+ assert_unreached("DOMException: InvalidStateError should have been thrown.");
+ });
+ } catch (e) {
+ test_pointerEvent.step(function () {
+ assert_equals(e.name, "InvalidStateError", "DOMException should be InvalidStateError");
+ });
+ }
+ } else if (mouse_move_count == 3) {
+ document.exitPointerLock();
+ mouse_move_count = 0;
+ }
+
+ }
+ });
+ on_event(div2, 'gotpointercapture', function(event) {
+ capture_count++;
+ });
+ on_event(div2, 'lostpointercapture', function(event) {
+ capture_count++;
+ });
+ on_event(document, 'pointerlockchange', function(event) {
+ lock_change_count++;
+ test_pointerEvent.step(function() {
+ if (lock_change_count == 1)
+ assert_equals(document.pointerLockElement, div1, "document.pointerLockElement should be div1.");
+ else if (lock_change_count == 2)
+ assert_equals(document.pointerLockElement, null, "document.pointerLockElement should be null.");
+ });
+ });
+
+ injectInput().then(function(){
+ test_pointerEvent.step(function(){
+ assert_equals(lock_change_count, 2, "Pointer is unlocked");
+ assert_greater_than(mouse_move_count, 1, "More than 1 pointermove has been received after unlocked");
+ assert_equals(capture_count, 0, "There shouldn't be any capture events fired.");
+ });
+ test_pointerEvent.done();
+ })
+ }
+ // Inject mouse input
+ function injectInput() {
+ var actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: div1})
+ .pointerDown()
+ .pointerMove(30, 30, {origin: div1})
+ .pointerMove(60, 30, {origin: div1})
+ .pointerMove(30, 20, {origin: div1})
+ .pointerMove(10, 50, {origin: div1})
+ .pointerMove(40, 10, {origin: div1})
+ .pointerMove(5, 30, {origin: div1})
+ .pointerMove(-5, 15, {origin: div1})
+ .pointerUp()
+ .send();
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events pointer lock test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks that we do not set the pointer capture when any element in the page gets a pointer lock.
+ <ol>
+ <li>Press left button down on the green rectangle and hold it.</li>
+ <li>Move the mouse inside the green rectangle.</li>
+ </ol>
+
+ Test passes if the pointer capture is not set when the green rectangle gets the pointer lock.
+ </h4>
+ <div id="testContainer">
+ <div id="div1" style="width:800px;height:250px;background:green"></div>
+ <div id="div2" style="width:800px;height:250px;background:yellow"></div>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html
new file mode 100644
index 0000000000..f4155180b8
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointermove</title>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="/resources/testdriver.js"></script>
+ <script type="text/javascript" src="/resources/testdriver-actions.js"></script>
+ <script type="text/javascript" src="/resources/testdriver-vendor.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h2>pointerlock</h2>
+ <h4>Test Description: This test checks if pointermove is dispatched correctly while in pointerlock mode.
+ <ol>
+ <li>Click in the black box (and accept the pointerlock permission if asked).</li>
+ <li>Move your mouse.</li>
+ <li>Click in the purple box inside the iframe</li>
+ <li>Move your mouse.</li>
+ </ol>
+ </h4>
+ <div id="target0"></div>
+ <iframe id="innerframe" onload="iframe_loaded()" src="resources/pointerevent_pointermove_in_pointerlock-iframe.html"></iframe>
+ <script>
+ window.name="outerframe";
+ var test_pointermove = async_test("pointermove event received");
+ var test_pointermove_innerframe = async_test("pointermove event received inner frame");
+ var actions_promise1;
+ var actions_promise2;
+ var actions_promise3;
+ var actions_promise4;
+ PhaseEnum = {
+ Start: 0,
+ Lock1: 1,
+ Lock2: 2,
+ Done: 3,
+ };
+ var iframe_loaded_promise;
+ var iframe_loaded_resolve;
+
+ iframe_loaded_promise = new Promise((resolve, reject)=>{
+ iframe_loaded_resolve = resolve;
+ });
+
+ function iframe_loaded(){
+ iframe_loaded_resolve();
+ }
+
+ async function run() {
+ // wait for iframe to fully load
+ await iframe_loaded_promise;
+ var target0 = document.getElementById("target0");
+ var innerframe = document.getElementById('innerframe');
+ var target1 = innerframe.contentDocument.getElementById('target1');
+ innerframe.contentWindow.name = "innerframe";
+ phase = PhaseEnum.Start;
+
+ on_event(target0, "click", async (event)=>{
+ if(phase === PhaseEnum.Start){
+ target0.requestPointerLock();
+ }else{
+ await actions_promise2;
+ document.exitPointerLock();
+ }
+ });
+
+ on_event(target1, "click", async (event)=>{
+ if(phase === PhaseEnum.Lock1){
+ target1.requestPointerLock();
+ }
+ else{
+ await actions_promise4;
+ innerframe.contentDocument.exitPointerLock();
+ }
+ });
+
+ on_event(target0, "pointermove", async (event)=> {
+ if (phase === PhaseEnum.Lock1) {
+ test_pointermove.step(()=> {
+ assert_equals(document.pointerLockElement, target0);
+ assert_equals(event.view.name, "outerframe", "View attribute of pointermove should be the target frame.");
+ }, "View attribute of pointermove should be the target frame.");
+ }
+ });
+
+ on_event(target1, "pointermove", async (event)=> {
+ if (phase === PhaseEnum.Lock2) {
+ test_pointermove_innerframe.step(()=> {
+ assert_equals(innerframe.contentDocument.pointerLockElement, target1);
+ assert_equals(event.view.name, "innerframe", "View attribute of pointermove should be the target frame.");
+ }, "View attribute of pointermove should be the target frame.");
+ }
+ });
+
+ on_event(document, "pointerlockchange", async (event)=> {
+ await actions_promise1;
+ if (phase === PhaseEnum.Start) {
+ test_pointermove.step(()=>{
+ assert_equals(document.pointerLockElement, target0);
+ });
+ phase++;
+ // Send moves in main frame target and then click on target0
+ actions_promise2 = new test_driver.Actions()
+ .pointerMove(10, 30, {origin: target0})
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }else{
+ // when we exit pointerlock we are done
+ test_pointermove.done();
+ // user activate innerframe
+ actions_promise3 = clickInTarget("mouse", target1);
+ }
+ });
+
+ on_event(innerframe.contentDocument, "pointerlockchange", async (event)=> {
+ await actions_promise3;
+ if (phase === PhaseEnum.Lock1) {
+ test_pointermove_innerframe.step(()=>
+ assert_equals(innerframe.contentDocument.pointerLockElement, target1));
+ phase++;
+ // Send moves in inner frame target and then click anywhere in the inner frame
+ await new test_driver.Actions()
+ .pointerMove(10, 30, {origin: target1})
+ .send();
+ actions_promise4 = clickInTarget("mouse", target1);
+ }
+ else{
+ // when we exit pointerlock we are done
+ test_pointermove_innerframe.done();
+ }
+ });
+
+ on_event(document, "pointerlockerror", test_pointermove.unreached_func("pointer lock request should not fail in document!"));
+ on_event(innerframe.contentDocument, "pointerlockerror", test_pointermove_innerframe.unreached_func("pointer lock request should not fail in innerframe.contentDocument!"));
+
+ // Click the outer frame target to lock.
+ actions_promise1 = clickInTarget("mouse", target0);
+ }
+ </script>
+ <div id="complete-notice">
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html
new file mode 100644
index 0000000000..8c362a6fc5
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointermove on button state changes</title>
+ <meta name="viewport" content="width=device-width">
+ <meta name="assert" content="When a pointer changes button state and does not produce a different event, the pointermove event must be dispatched."/>
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>PointerMove</h2>
+ <h4>Test Description: This test checks if pointermove event are triggered by button state changes
+ <ol>
+ <li>Click on the black rectangle to lock the pointer </li>
+ <li>Press a button and hold it</li>
+ <li>Press a second button</li>
+ <li>Release the second button</li>
+ <li>Release the first button to complete the test</li>
+ </ol>
+ </h4>
+ <div id="target0" style="background:black"></div>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointermove = async_test("pointer locked pointermove events received for button state changes");
+ add_completion_callback(showPointerTypes);
+ var actions_promise;
+
+ var step = 0;
+ var firstButton = 0;
+ var pointer_locked = false;
+
+ async function run() {
+ var target0 = document.getElementById("target0");
+
+ // When a pointer changes button state and the circumstances produce no other pointer event, the pointermove event must be dispatched.
+ // 5.2.6
+
+ on_event(target0, "pointerdown", function (event) {
+ if (pointer_locked) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointermove.step(function() {
+ assert_equals(step, 0, "There must not be more than one pointer down event.");
+ });
+ if (step == 0) {
+ step = 1;
+ firstButton = event.buttons;
+ }
+ }
+ });
+ on_event(target0, "pointermove", function (event) {
+ if (pointer_locked) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (step == 1 && event.button != -1) { // second button pressed
+ test_pointermove.step(function() {
+ assert_not_equals(event.buttons, firstButton, "The pointermove event must be triggered by pressing a second button.");
+ });
+ test_pointermove.step(function() {
+ assert_true((event.buttons & firstButton) != 0, "The first button must still be reported pressed.");
+ });
+ step = 2;
+ } else if (step == 2 && event.button != -1) { // second button released
+ test_pointermove.step(function() {
+ assert_equals(event.buttons, firstButton, "The pointermove event must be triggered by releasing the second button.");
+ });
+ step = 3;
+ }
+ }
+ });
+ on_event(target0, "pointerup", function (event) {
+ if (pointer_locked) {
+ detected_pointertypes[event.pointerType] = true;
+ test_pointermove.step(function() {
+ assert_equals(step, 3, "The pointerup event must be triggered after pressing and releasing the second button.");
+ });
+ test_pointermove.step(function() {
+ assert_equals(event.buttons, 0, "The pointerup event must be triggered by releasing the last pressed button.");
+ });
+ document.exitPointerLock();
+ } else {
+ target0.requestPointerLock();
+ }
+ });
+ on_event(document, 'pointerlockchange', function(event) {
+ if (document.pointerLockElement == target0)
+ pointer_locked = true;
+ else{
+ pointer_locked = false;
+ actions_promise.then( () => {
+ test_pointermove.done();
+ });
+ }
+ });
+ on_event(target0, "mouseup", function (event) {
+ event.preventDefault();
+ });
+ on_event(target0, "contextmenu", function (event) {
+ event.preventDefault();
+ });
+
+ // Inject mouse input
+ var actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ await actions_promise;
+ await resolveWhen(()=>pointer_locked);
+ actions = new test_driver.Actions();
+ actions_promise = actions
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+ </script>
+ <h1>Pointer Lock Pointer Events pointermove on button state changes Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerrawupdate_in_pointerlock.https.html b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerrawupdate_in_pointerlock.https.html
new file mode 100644
index 0000000000..73aa4e5a58
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/pointerevent_pointerrawupdate_in_pointerlock.https.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerrawupdate</title>
+ <meta name="viewport" content="width=device-width">
+ <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>
+ </head>
+ <body onload="run()">
+ <h2>pointerrawupdate</h2>
+ <h4>Test Description: This test checks if pointerrawupdate is dispatched correctly while in pointerlock mode.
+ <ol>
+ <li>Click in the black box (and accept the pointerlock permission if asked).</li>
+ <li>Move your mouse.</li>
+ <li>Click in the purple box inside the iframe</li>
+ <li>Move your mouse.</li>
+ </ol>
+ </h4>
+ <div id="target0"></div>
+ <iframe id="innerframe" src="../resources/pointerevent_pointerrawupdate_in_pointerlock-iframe.html"></iframe>
+ <script>
+ window.name="outerframe";
+ var test_pointerrawupdate = async_test("pointerrawupdate event received");
+
+ var outerframe_pointerrawupdateReceived = false;
+ var innerframe_pointerrawupdateReceived = false;
+
+ async function run() {
+ var target0 = document.getElementById("target0");
+ var innerframe = document.getElementById('innerframe');
+ var target1 = innerframe.contentDocument.getElementById('target1');
+
+ innerframe.contentWindow.name = "innerframe";
+
+ on_event(document, "pointerlockchange", function(event) {
+ if (document.pointerLockElement == target0) {
+ on_event(target0, "pointerrawupdate", function (event) {
+ outerframe_pointerrawupdateReceived = true;
+ test_pointerrawupdate.step(function() {
+ assert_equals(event.view.name, "outerframe", "View attribute of pointerrawupdate should be the target frame. target0 pointerrawupdate");
+ }, "View attribute of pointerrawupdate should be the target frame.");
+ });
+ on_event(target0, "pointermove", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_true(outerframe_pointerrawupdateReceived,
+ "Pointerrawupdate event should have been received before pointermove while in pointerlock mode.");
+ assert_equals(event.view.name, "outerframe", "View attribute of pointerrawupdate should be the target frame. target0 pointermove");
+ }, "Pointerrawupdate event should have been received before pointermove while in pointerlock mode.");
+ document.exitPointerLock();
+
+ on_event(target1, "click", function(event) {
+ target1.requestPointerLock();
+ });
+
+ on_event(innerframe.contentDocument, "pointerlockchange", function(event) {
+ if (innerframe.contentDocument.pointerLockElement == target1) {
+ on_event(target1, "pointerrawupdate", function (event) {
+ innerframe_pointerrawupdateReceived = true;
+ test_pointerrawupdate.step(function() {
+ assert_equals(event.view.name, "innerframe", "View attribute of pointerrawupdate should be the target frame. target1");
+ }, "View attribute of pointerrawupdate should be the target frame.");
+ });
+ on_event(target1, "pointermove", function (event) {
+ test_pointerrawupdate.step(function() {
+ assert_true(innerframe_pointerrawupdateReceived,
+ "Pointerrawupdate event should have been received before pointermove while in pointerlock mode.");
+ }, "Pointerrawupdate event should have been received before pointermove while in pointerlock mode.");
+ innerframe.contentDocument.exitPointerLock();
+ test_pointerrawupdate.done();
+ });
+ }
+ });
+
+ });
+ }
+ });
+ on_event(target0, "click", function(event) {
+ target0.requestPointerLock();
+ });
+
+ // Inject input
+ var iframeRect = innerframe.getClientRects()[0];
+ var rect = target1.getClientRects()[0];
+ var center_x = Math.round(iframeRect.left + (rect.left + rect.right) / 2);
+ var center_y = Math.round(iframeRect.top + (rect.top + rect.bottom) / 2);
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target0})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(10, 0, {origin: target0})
+ .pointerMove(center_x, center_y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(center_x + 10, center_y)
+ .send();
+ }
+
+ </script>
+ <div id="complete-notice">
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html b/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html
new file mode 100644
index 0000000000..627af3b61c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width">
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_pointermove_in_pointerlock-iframe.html b/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_pointermove_in_pointerlock-iframe.html
new file mode 100644
index 0000000000..699fb39b8b
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerlock/resources/pointerevent_pointermove_in_pointerlock-iframe.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../../pointerevent_styles.css">
+ </head>
+ <body>
+ <div id="target1"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/pointerup_after_pointerdown_target_removed.html b/testing/web-platform/tests/pointerevents/pointerup_after_pointerdown_target_removed.html
new file mode 100644
index 0000000000..65c6ea314c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerup_after_pointerdown_target_removed.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<title>pointerup event fired after pointerdown target is removed</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?touch">
+<meta name="variant" content="?pen">
+<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="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 50px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+ const pointer_type = location.search.substring(1);
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+
+ function logEvent(e) {
+ event_log.push(`${e.type}(${e.eventPhase})`);
+ }
+
+ parent.addEventListener("pointerup", logEvent);
+ parent.addEventListener("mouseup", logEvent);
+ child.addEventListener("pointerdown", e => e.target.remove(), {once:true});
+
+ promise_test(async () => {
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp();
+
+ await actions.send();
+ await done_click_promise;
+
+ assert_equals(event_log.toString(),
+ "pointerup(2),mouseup(2)",
+ "received events");
+ }, `pointerup event from ${pointer_type} fired after pointerdown target is removed`);
+</script>
diff --git a/testing/web-platform/tests/pointerevents/pointerup_button_value_matches_corresponding_pointerdown.html b/testing/web-platform/tests/pointerevents/pointerup_button_value_matches_corresponding_pointerdown.html
new file mode 100644
index 0000000000..1fb13f37d4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/pointerup_button_value_matches_corresponding_pointerdown.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<title>Button value of corresponding pointerup/pointerdown are equivalent</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="pointerevent_support.js"></script>
+
+<input id="target" style="margin: 20px">
+
+<script>
+ 'use strict';
+ const target = document.getElementById("target");
+
+ promise_test(async test => {
+ const test_pointer = "testPointer";
+
+ let actions = new test_driver.Actions();
+ actions = actions.addPointer(test_pointer, "mouse")
+ .pointerMove(0,0, {sourceName:test_pointer, origin:target})
+ .pointerDown({sourceName:test_pointer, button:actions.ButtonType.RIGHT})
+ .pointerUp({sourceName:test_pointer, button:actions.ButtonType.RIGHT});
+ target.addEventListener("contextmenu", event => { event.preventDefault(); });
+ let pointerdown_promise = getEvent("pointerdown", target, test);
+ let pointerup_promise = getEvent("pointerup", target, test);
+
+ await actions.send();
+ let pointerdown_event = await pointerdown_promise;
+ let pointerup_event = await pointerup_promise;
+
+ assert_equals(pointerup_event.button, pointerdown_event.button, "The 'button' property of a pointerup event should match the 'button' property of the corresponding pointerdown event");
+ }, "Test that the 'button' property of a pointerup event matches the 'button' property of the corresponding pointerdown event");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/predicted_events_attributes.html b/testing/web-platform/tests/pointerevents/predicted_events_attributes.html
new file mode 100644
index 0000000000..7049447eca
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/predicted_events_attributes.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<title>Predicted events count and properties</title>
+<meta name="variant" content="?mouse">
+<meta name="variant" content="?pen">
+<meta name="variant" content="?touch">
+<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>
+<script src="pointerevent_support.js"></script>
+<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="target"></div>
+<div id="done"></div>
+
+<script>
+ "use strict";
+ assert_true(location.search.length > 0,
+ "Test URL should contain pointer_type");
+ const pointer_type = location.search.substring(1);
+ assert_true(["mouse", "touch", "pen"].includes(pointer_type));
+ const target = document.getElementById("target");
+
+ // https://w3c.github.io/pointerevents/#predicted-events
+ function checkListAttributes(event) {
+ assert_equals(typeof event.getPredictedEvents, "function",
+ event.type + ".getPredictedEvents is a function");
+ assert_equals(typeof event.getPredictedEvents(), "object",
+ event.type + ".getPredictedEvents() returns an object");
+ if (event.type == "pointermove") {
+ assert_greater_than_equal(event.getPredictedEvents().length, 0,
+ event.type + ".getPredictedEvents() has 0 or more entry");
+ } else {
+ assert_equals(event.getPredictedEvents().length, 0,
+ event.type + ".getPredictedEvents() has 0 entry");
+ }
+ }
+
+ function actionToDragPointerInTarget() {
+ return new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerMove(20, 20, {origin: target})
+ .pointerUp()
+ }
+
+ promise_test(async () => {
+ const done = document.getElementById("done");
+
+ let pointerover_promise = getEvent("pointerover", target);
+ let pointerenter_promise = getEvent("pointerenter", target);
+ let pointerout_promise = getEvent("pointerout", target);
+ let pointerleave_promise = getEvent("pointerleave", target);
+
+ await clickInTarget(pointer_type, target);
+ await clickInTarget(pointer_type, done);
+
+ checkListAttributes(await pointerover_promise);
+ checkListAttributes(await pointerenter_promise);
+ checkListAttributes(await pointerout_promise);
+ checkListAttributes(await pointerleave_promise);
+ }, "Predicted list in boundary events");
+
+ promise_test(async () => {
+ // We need "touch-action:none" to guarantee pointermove events.
+ target.classList.add("touchActionNone");
+
+ target.addEventListener("pointerdown",
+ e => target.setPointerCapture(e.pointerId),
+ {once: true});
+
+ target.addEventListener("pointermove",
+ e => target.releasePointerCapture(e.pointerId),
+ {once: true});
+
+ let gotpointercapture_promise = getEvent("gotpointercapture", target);
+ let lostpointercapture_promise = getEvent("lostpointercapture", target);
+
+ await actionToDragPointerInTarget().send();
+
+ checkListAttributes(await gotpointercapture_promise);
+ checkListAttributes(await lostpointercapture_promise);
+
+ target.classList.remove("touchActionNone");
+ }, "Predicted list in pointer-capture events");
+
+ promise_test(async () => {
+ // We need "touch-action:none" to guarantee pointermove events.
+ target.classList.add("touchActionNone");
+
+ let pointerdown_promise = getEvent("pointerdown", target);
+ let pointermove_promise = getEvent("pointermove", target);
+ let pointerup_promise = getEvent("pointerup", target);
+
+ await actionToDragPointerInTarget().send();
+
+ checkListAttributes(await pointerdown_promise);
+ checkListAttributes(await pointermove_promise);
+ checkListAttributes(await pointerup_promise);
+
+ target.classList.remove("touchActionNone");
+ }, "Predicted list in pointerdown/move/up events");
+
+ promise_test(async () => {
+ if (pointer_type !== "touch") {
+ assert_true(true, "Skipped for " + pointer_type);
+ return;
+ }
+
+ let pointercancel_promise = getEvent("pointercancel", target);
+
+ await actionToDragPointerInTarget().send();
+
+ checkListAttributes(await pointercancel_promise);
+ }, "Predicted list in pointercancel event");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/resources/iframe-touch-action-none-subframe.html b/testing/web-platform/tests/pointerevents/resources/iframe-touch-action-none-subframe.html
new file mode 100644
index 0000000000..0a894d4401
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/iframe-touch-action-none-subframe.html
@@ -0,0 +1,26 @@
+<html>
+ <style>
+ body {
+ width: 200px;
+ height: 150px;
+ padding: 0px;
+ margin: 0px;
+ background-color: lightgreen
+ }
+ </style>
+ <body></body>
+ <script>
+ function handler(e) {
+ window.top.postMessage({
+ "type": "subframe-event",
+ "eventType": e.type
+ }, "*");
+ }
+
+ ["pointerup", "pointercancel"].forEach(eventType => {
+ document.body.addEventListener(
+ eventType,
+ handler);
+ });
+ </script>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/resources/minimal.html b/testing/web-platform/tests/pointerevents/resources/minimal.html
new file mode 100644
index 0000000000..9402b95cb1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/minimal.html
@@ -0,0 +1,4 @@
+<body>Minimal HTML</body>
+<script>
+ parent.postMessage({"type": "subframe-loaded"}, "*");
+</script>
diff --git a/testing/web-platform/tests/pointerevents/resources/pointerevent_fractional_coordinates-iframe.html b/testing/web-platform/tests/pointerevents/resources/pointerevent_fractional_coordinates-iframe.html
new file mode 100644
index 0000000000..5245a3f2e1
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/pointerevent_fractional_coordinates-iframe.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ </head>
+ <style>
+ .square {
+ width: 3px;
+ height:3px;
+ background: black;
+ cursor: pointer;
+ }
+ #s1 {
+ top: 10px;
+ left: 10px;
+ }
+ #s2 {
+ top: 30px;
+ left: 50px;
+ }
+ #s3 {
+ top: 50px;
+ left: 30px;
+ }
+ </style>
+ <body>
+ <div id="s1" class="square"></div>
+ <div id="s2" class="square"></div>
+ <div id="s3" class="square"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html b/testing/web-platform/tests/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
new file mode 100644
index 0000000000..3c88328b00
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
@@ -0,0 +1,3 @@
+<body id='innerFrame' style='height:500px; width: 500px; padding: 0; margin: 0;'>
+ <div id = 'target'></div>
+</body>
diff --git a/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerId_scope-iframe.html b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerId_scope-iframe.html
new file mode 100644
index 0000000000..c1fa22aa4c
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerId_scope-iframe.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+ <!--
+Test cases for Pointer Events v1 spec
+This document references Test Assertions (abbrev TA below) written by Cathy Chan
+http://www.w3.org/wiki/PointerEvents/TestAssertions
+-->
+ <head>
+ <title>Pointer Events pointerdown tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script>
+ function run() {
+ var target1 = document.getElementById("target1");
+
+ var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
+
+ eventList.forEach(function(eventName) {
+ target1.addEventListener(eventName, function (event) {
+ var pass_data = {
+ 'pointerId' : event.pointerId,
+ 'type' : event.type,
+ 'pointerType' : event.pointerType
+ };
+ top.postMessage(pass_data, "*");
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <div id="target1" class="touchActionNone">
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/resources/pointerevent_pointercapture-iframe.html b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointercapture-iframe.html
new file mode 100644
index 0000000000..1007566269
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointercapture-iframe.html
@@ -0,0 +1,14 @@
+<html id='innerFrameDocument'>
+<style>
+body {
+ touch-action:none;
+}
+</style>
+ <body id='innerFrame' style='height:500px; width: 500px; padding: 0; margin: 0;'>
+ <script>
+ top.document.testEventList.forEach(function(eventName) {
+ document.addEventListener(eventName, top.handleEvent);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerrawupdate_in_pointerlock-iframe.html b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerrawupdate_in_pointerlock-iframe.html
new file mode 100644
index 0000000000..505fc2cae4
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/resources/pointerevent_pointerrawupdate_in_pointerlock-iframe.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ </head>
+ <body>
+ <div id="target1"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/pointerevents/touch-action-with-swipe-dir-change.html b/testing/web-platform/tests/pointerevents/touch-action-with-swipe-dir-change.html
new file mode 100644
index 0000000000..69d8b099d0
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/touch-action-with-swipe-dir-change.html
@@ -0,0 +1,115 @@
+<!doctype html>
+<title>touch-action behavior with swipe direction changes</title>
+<meta name="variant" content="?touch">
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://w3c.github.io/pointerevents/#determining-supported-direct-manipulation-behavior" />
+<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="pointerevent_support.js"></script>
+<style>
+ #target {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ }
+ #spacer {
+ width: 400px;
+ height: 400px;
+ }
+ #done {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="target">
+ <div id="spacer"></div>
+</div>
+<div id="done"></div>
+
+<script>
+ "use strict";
+ const pointer_type = "touch";
+ const target = document.getElementById("target");
+ const done = document.getElementById("done");
+
+ assert_true(location.search.length > 0 &&
+ location.search.substring(1) === pointer_type,
+ "Test URL has 'touch' pointer-type");
+
+ /*
+ For each promise_test, we have these 5 parameters in order:
+ - touch-action value to test,
+ - a list of directions ("right" or "down") in the swipe to test,
+ - whether scrollLeft change is expected, and
+ - whether scrollTop change is expected.
+ */
+ const promise_test_params = [
+ ["auto", ["right", "down"], true, true ],
+ ["auto", ["down", "right"], true, true ],
+ ["pan-x", ["right", "down"], true, false],
+ ["pan-x", ["down", "right"], false, false],
+ ["pan-y", ["right", "down"], false, false],
+ ["pan-y", ["down", "right"], false, true ],
+ ["none" , ["right", "down"], false, false],
+ ["none" , ["down", "right"], false, false],
+ ];
+
+ function appendSwipeActions(actions, directions) {
+ const deltas = {
+ "right": [30, 0],
+ "down": [0, 30],
+ }
+ let pos = [0, 0];
+ for (const direction of directions) {
+ // Move three steps along each direction.
+ for (let i = 0; i < 3; i++) {
+ pos[0] += deltas[direction][0];
+ pos[1] += deltas[direction][1];
+ actions = actions.pointerMove(pos[0], pos[1], {origin: target});
+ }
+ }
+ return actions;
+ }
+
+ for (let i = 0; i < promise_test_params.length; i++) {
+ let [touch_action, directions, left_change_expected, top_change_expected]
+ = promise_test_params[i];
+
+ promise_test(async () => {
+ target.style.touchAction = touch_action;
+
+ // Reset the scroll position to 50% mark on both axes.
+ target.scrollLeft = 100;
+ target.scrollTop = 100;
+ const done_pointerup_promise = getEvent("pointerup", done);
+
+ let actions = new test_driver.Actions()
+ .addPointer("TestPointer", pointer_type)
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown();
+ actions = appendSwipeActions(actions, directions);
+ actions = actions.pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp()
+
+ await actions.send();
+ await done_pointerup_promise;
+
+ if (left_change_expected) {
+ assert_less_than(target.scrollLeft, 100, "scrollLeft should change");
+ } else {
+ assert_equals(target.scrollLeft, 100, "scrollLeft should not change");
+ }
+
+ if (top_change_expected) {
+ assert_less_than(target.scrollTop, 100, "scrollTop should change");
+ } else {
+ assert_equals(target.scrollTop, 100, "scrollTop should not change");
+ }
+ }, `touch-action:${touch_action} with ${directions} swipe`);
+ }
+</script>