Fix click.pointerId for touch events sent to subframes. (#31597)
Blink's pointerId generation is attached to low-level PointerEvent
handling (like pointerdown/up/move) which stores the data in the
local-frame-root. However, to find a touch click's pointerId (which is
needed in an asynchronous manner), we have been accessing at the
subframe's state because of a historic crack in Blink EventHandler code
(https://crbug.com/449649). This CL fixes the id-data lookup to access
local-frame-root's state instead.
Bug: 1264930
Change-Id: I9d2a76dd18c63086afd253575f99bf7f4f26bc7d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3273473
Reviewed-by: Robert Flack <flackr@chromium.org>
Commit-Queue: Mustaq Ahmed <mustaq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#946622}
Co-authored-by: Mustaq Ahmed <mustaq@google.com>
diff --git a/pointerevents/pointerevent_click_is_a_pointerevent.html b/pointerevents/pointerevent_click_is_a_pointerevent.html
index 0ce9f07..46c1ed4 100644
--- a/pointerevents/pointerevent_click_is_a_pointerevent.html
+++ b/pointerevents/pointerevent_click_is_a_pointerevent.html
@@ -9,50 +9,68 @@
<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';
-let target = document.getElementById("target");
-let pointerId = 0;
-let pointerType = "";
-let inputSource = location.search.substring(1);
+const pointer_type = location.search.substring(1);
+let subframe_loaded = getMessageData("subframe-loaded", frames[0]);
-target.addEventListener("pointerdown", (e)=>{
- pointerId = e.pointerId;
- pointerType = e.pointerType;
-});
-
-function testFunction(test){
- return test.step_func(e=>{
- assert_equals(e.constructor, window.PointerEvent, "click should use a PointerEvent constructor");
- assert_true(e instanceof PointerEvent, "click should be a PointerEvent");
- assert_equals(e.pointerId, pointerId, "click's pointerId should match the pointerId of the pointer event that triggers it");
- assert_equals(e.pointerType, pointerType, "click's pointerType should match the pointerType of the pointer event that triggers it");
- });
+function assert_click_construction(click_event, window_object) {
+ 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");
}
-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 = 0;
- pointerType = "";
- });
- 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});
- Promise.all([eventWatcher.wait_for("click"), actions.send()]).then(()=>resolve());
- }), "click using " + pointerType + " is a PointerEvent");
+function assert_click_attributes(click_event, pointerdown_event, pointerup_event) {
+ assert_equals(click_event.pointerId, pointerdown_event.pointerId,
+ "click.pointerId should match the pointerId of the triggering pointerdown");
+ assert_equals(click_event.pointerType, pointerdown_event.pointerType,
+ "click.pointerType should match the pointerType of the triggering pointerdown");
+
+ assert_equals(click_event.pointerId, pointerup_event.pointerId,
+ "click.pointerId should match the pointerId of the triggering pointerup");
+ assert_equals(click_event.pointerType, pointerup_event.pointerType,
+ "click.pointerType should match the pointerType of the triggering pointerup");
}
-run_test(inputSource);
+promise_test(async () => {
+ const target = document.getElementById("target");
+
+ let pointerdown_promise = getEvent("pointerdown", target);
+ let pointerup_promise = getEvent("pointerup", target);
+ let click_promise = getEvent("click", target);
+
+ await clickInTarget(pointer_type, target);
+
+ let pointerdown_event = await pointerdown_promise;
+ let pointerup_event = await pointerup_promise;
+ let click_event = await click_promise;
+
+ assert_click_construction(click_event, this);
+ assert_click_attributes(click_event, pointerdown_event, pointerup_event);
+}, "click using " + pointer_type + " is a PointerEvent");
+
+promise_test(async () => {
+ await subframe_loaded;
+
+ const target = frames[0];
+ let pointerdown_promise = getEvent("pointerdown", target);
+ let pointerup_promise = getEvent("pointerup", target);
+ let click_promise = getEvent("click", target);
+
+ 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;
+
+ assert_click_construction(click_event, frames[0]);
+ assert_click_attributes(click_event, pointerdown_event, pointerup_event);
+}, "click in a subframe using " + pointer_type + " is a PointerEvent");
</script>
diff --git a/pointerevents/pointerevent_support.js b/pointerevents/pointerevent_support.js
index 6cbc8d6..727909f 100644
--- a/pointerevents/pointerevent_support.js
+++ b/pointerevents/pointerevent_support.js
@@ -453,3 +453,26 @@
return true;
}
+
+// Returns a |Promise| that gets resolved with the event object when |target|
+// receives an event of type |event_type|.
+function getEvent(event_type, target) {
+ return new Promise(resolve => {
+ target.addEventListener(event_type, e => resolve(e), {once: true});
+ });
+}
+
+// Returns a |Promise| that gets resolved with |event.data| when |window|
+// receives from |source| a "message" event whose |event.data.type| matches the string
+// |message_data_type|.
+function getMessageData(message_data_type, source) {
+ return new Promise(resolve => {
+ function waitAndRemove(e) {
+ if (e.source != source || !e.data || e.data.type != message_data_type)
+ return;
+ window.removeEventListener("message", waitAndRemove);
+ resolve(e.data);
+ }
+ window.addEventListener("message", waitAndRemove);
+ });
+}
diff --git a/pointerevents/resources/minimal.html b/pointerevents/resources/minimal.html
new file mode 100644
index 0000000..9402b95
--- /dev/null
+++ b/pointerevents/resources/minimal.html
@@ -0,0 +1,4 @@
+<body>Minimal HTML</body>
+<script>
+ parent.postMessage({"type": "subframe-loaded"}, "*");
+</script>