0

[Interop 2023] Make PE/ME enter/leave events non-composed

Chrome always had mouse/pointer enter/leave events as
"composed", which was matching the specs in the past.
However, the specs were updated in the last few years to
make these events non-composed:
  https://github.com/w3c/uievents/pull/210
  https://github.com/w3c/pointerevents/pull/461
Firefox was update too:
https://bugzilla.mozilla.org/show_bug.cgi?id=1484371

This CL makes Chrome spec compliant.

Fixed: 876994, 874082, 1136584
Change-Id: I861b15cd71bc9c06e28d5d325f8b72c72d7cde16
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4536900
Reviewed-by: Mustaq Ahmed <mustaq@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Commit-Queue: Mustaq Ahmed <mustaq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1146033}
This commit is contained in:
Mustaq Ahmed
2023-05-18 17:40:47 +00:00
committed by Chromium LUCI CQ
parent 90f924d052
commit 7a27f6bc74
8 changed files with 55 additions and 31 deletions

@@ -273,8 +273,9 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent(
void PointerEventFactory::SetEventSpecificFields( void PointerEventFactory::SetEventSpecificFields(
PointerEventInit* pointer_event_init, PointerEventInit* pointer_event_init,
const AtomicString& type) { const AtomicString& type) {
pointer_event_init->setBubbles(type != event_type_names::kPointerenter && bool is_pointer_enter_or_leave = type == event_type_names::kPointerenter ||
type != event_type_names::kPointerleave); type == event_type_names::kPointerleave;
pointer_event_init->setBubbles(!is_pointer_enter_or_leave);
pointer_event_init->setCancelable( pointer_event_init->setCancelable(
type != event_type_names::kPointerenter && type != event_type_names::kPointerenter &&
type != event_type_names::kPointerleave && type != event_type_names::kPointerleave &&
@@ -282,8 +283,10 @@ void PointerEventFactory::SetEventSpecificFields(
type != event_type_names::kPointerrawupdate && type != event_type_names::kPointerrawupdate &&
type != event_type_names::kGotpointercapture && type != event_type_names::kGotpointercapture &&
type != event_type_names::kLostpointercapture); type != event_type_names::kLostpointercapture);
pointer_event_init->setComposed(
pointer_event_init->setComposed(true); RuntimeEnabledFeatures::NonComposedEnterLeaveEventsEnabled()
? !is_pointer_enter_or_leave
: true);
pointer_event_init->setDetail(0); pointer_event_init->setDetail(0);
} }

@@ -91,7 +91,10 @@ void SetMouseEventAttributes(MouseEventInit* initializer,
initializer->setButtons( initializer->setButtons(
MouseEvent::WebInputEventModifiersToButtons(mouse_event.GetModifiers())); MouseEvent::WebInputEventModifiersToButtons(mouse_event.GetModifiers()));
initializer->setView(target_node->GetDocument().domWindow()); initializer->setView(target_node->GetDocument().domWindow());
initializer->setComposed(true); initializer->setComposed(
RuntimeEnabledFeatures::NonComposedEnterLeaveEventsEnabled()
? !is_mouse_enter_or_leave
: true);
initializer->setDetail(click_count); initializer->setDetail(click_count);
initializer->setRelatedTarget(related_target); initializer->setRelatedTarget(related_target);
UIEventWithKeyState::SetFromWebInputEventModifiers( UIEventWithKeyState::SetFromWebInputEventModifiers(
@@ -996,6 +999,10 @@ bool MouseEventManager::TryStartDrag(
WebInputEventResult MouseEventManager::DispatchDragSrcEvent( WebInputEventResult MouseEventManager::DispatchDragSrcEvent(
const AtomicString& event_type, const AtomicString& event_type,
const WebMouseEvent& event) { const WebMouseEvent& event) {
CHECK(event_type == event_type_names::kDrag ||
event_type == event_type_names::kDragend ||
event_type == event_type_names::kDragstart);
return DispatchDragEvent(event_type, GetDragState().drag_src_.Get(), nullptr, return DispatchDragEvent(event_type, GetDragState().drag_src_.Get(), nullptr,
event, GetDragState().drag_data_transfer_.Get()); event, GetDragState().drag_data_transfer_.Get());
} }

@@ -2378,6 +2378,13 @@
status: "test", status: "test",
base_feature: "none", base_feature: "none",
}, },
// Makes enter/leave Mouse and Pointer Events non-composed as per
// corresponding specifications.
{
name: "NonComposedEnterLeaveEvents",
public: true,
status: "experimental",
},
{ {
// Kill switch for crbug.com/1427047 fix. // Kill switch for crbug.com/1427047 fix.
// TODO(xiaochengh): Remove in M117. // TODO(xiaochengh): Remove in M117.

@@ -3296,15 +3296,6 @@ crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.html [ Failu
crbug.com/626703 external/wpt/media-source/mediasource-correct-frames-after-reappend.html [ Failure Pass Timeout ] crbug.com/626703 external/wpt/media-source/mediasource-correct-frames-after-reappend.html [ Failure Pass Timeout ]
crbug.com/626703 external/wpt/media-source/mediasource-correct-frames.html [ Failure Pass Timeout ] crbug.com/626703 external/wpt/media-source/mediasource-correct-frames.html [ Failure Pass Timeout ]
crbug.com/626703 external/wpt/payment-method-basic-card/steps_for_selecting_the_payment_handler.html [ Timeout ] crbug.com/626703 external/wpt/payment-method-basic-card/steps_for_selecting_the_payment_handler.html [ Timeout ]
crbug.com/876994 external/wpt/pointerevents/pointerevent_attributes_hoverable_pointers.html?mouse [ Failure ]
crbug.com/876994 external/wpt/pointerevents/pointerevent_attributes_hoverable_pointers.html?pen [ Failure ]
crbug.com/876994 external/wpt/pointerevents/pointerevent_attributes_nohover_pointers.html [ Failure ]
crbug.com/876994 external/wpt/pointerevents/pointerevent_attributes_hoverable_rightbutton.html?mouse [ Failure ]
crbug.com/876994 external/wpt/pointerevents/pointerevent_attributes_hoverable_rightbutton.html?pen [ Failure ]
crbug.com/876994 external/wpt/uievents/mouse/attributes.html [ Failure ]
crbug.com/1136584 external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?mouse [ Failure ]
crbug.com/1136584 external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?pen [ Failure ]
crbug.com/1136584 external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?touch [ Failure ]
crbug.com/626703 external/wpt/payment-method-basic-card/apply_the_modifiers.html [ Timeout ] crbug.com/626703 external/wpt/payment-method-basic-card/apply_the_modifiers.html [ Timeout ]
crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Skip Timeout ] crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Skip Timeout ]
crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Skip Timeout ] crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Skip Timeout ]

@@ -26,10 +26,15 @@ target.addEventListener("pointerdown", (e)=>{
function testFunction(test){ function testFunction(test){
return test.step_func(e=>{ return test.step_func(e=>{
assert_equals(e.constructor, window.PointerEvent, "auxclick should use a PointerEvent constructor"); assert_equals(e.constructor, window.PointerEvent,
assert_true(e instanceof PointerEvent, "auxclick should be a PointerEvent"); "auxclick should use a PointerEvent constructor");
assert_equals(e.pointerId, pointerId, "auxclick's pointerId should match the pointerId of the pointer event that triggers it"); assert_true(e instanceof PointerEvent,
assert_equals(e.pointerType, pointerType, "axclick's pointerType should match the pointerType of the pointer event that triggers it"); "auxclick should be a PointerEvent");
assert_equals(e.pointerId, pointerId,
"auxclick's pointerId should match the pointerId of the pointer event that triggers it");
assert_equals(e.pointerType, pointerType,
"auxclick's pointerType should match the pointerType of the pointer event that triggers it");
assert_equals(e.composed, true, "auxclick.composed should be true");
}); });
} }
@@ -51,10 +56,15 @@ function run_test(pointerType){
.pointerMove(0,0, {origin:target, sourceName:testPointer}) .pointerMove(0,0, {origin:target, sourceName:testPointer})
.pointerDown({button:actions.ButtonType.MIDDLE, sourceName:testPointer}) .pointerDown({button:actions.ButtonType.MIDDLE, sourceName:testPointer})
.pointerUp({button:actions.ButtonType.MIDDLE, sourceName:testPointer}); .pointerUp({button:actions.ButtonType.MIDDLE, sourceName:testPointer});
Promise.all([pointerDownPrevented, eventWatcher.wait_for("auxclick"), actions.send()]).then(()=>resolve()); Promise.all([
pointerDownPrevented,
eventWatcher.wait_for("auxclick"),
actions.send()
]).then(()=>resolve());
}), "auxclick using " + pointerType + " is a PointerEvent"); }), "auxclick using " + pointerType + " is a PointerEvent");
} }
run_test(inputSource); run_test(inputSource);
// TODO(crbug.com/1150441): Add test for auxclick from touch.Note: Calling run_test("touch") here times out. // TODO(crbug.com/1150441): Add test for auxclick from touch. Note: Calling
// run_test("touch") here times out.
</script> </script>

@@ -25,6 +25,7 @@ function assert_click_construction(click_event, window_object) {
"click should use a PointerEvent constructor"); "click should use a PointerEvent constructor");
assert_true(click_event instanceof window_object.PointerEvent, assert_true(click_event instanceof window_object.PointerEvent,
"click should be a PointerEvent instance"); "click should be a PointerEvent instance");
assert_equals(click_event.composed, true, "click.composed should be true");
} }
function assert_click_attributes(click_event, pointerdown_event, pointerup_event) { function assert_click_attributes(click_event, pointerdown_event, pointerup_event) {
@@ -36,7 +37,7 @@ function assert_click_attributes(click_event, pointerdown_event, pointerup_event
assert_equals(click_event.pointerId, pointerup_event.pointerId, assert_equals(click_event.pointerId, pointerup_event.pointerId,
"click.pointerId should match the pointerId of the triggering pointerup"); "click.pointerId should match the pointerId of the triggering pointerup");
assert_equals(click_event.pointerType, pointerup_event.pointerType, assert_equals(click_event.pointerType, pointerup_event.pointerType,
"click.pointerType should match the pointerType of the triggering pointerup"); "click.pointerType should match the pointerType of the triggering pointerup");
} }
promise_test(async () => { promise_test(async () => {

@@ -20,10 +20,15 @@ let pointerdownPointerId, pointerdownPointerType;
let inputSource = location.search.substring(1); let inputSource = location.search.substring(1);
target.addEventListener("contextmenu", contextmenuTest.step_func((e)=>{ target.addEventListener("contextmenu", contextmenuTest.step_func((e)=>{
assert_equals(e.constructor, window.PointerEvent, "contextmenu should use a PointerEvent constructor"); assert_equals(e.constructor, window.PointerEvent,
assert_true(e instanceof PointerEvent, "contextmenu should be a PointerEvent"); "contextmenu should use a PointerEvent constructor");
assert_equals(e.pointerId, pointerdownPointerId, "contextmenu's pointerId should match the pointerId of the pointer event that triggers it"); assert_true(e instanceof PointerEvent,
assert_equals(e.pointerType, pointerdownPointerType, "contextmenu's pointerType should match the pointerType of the pointer event that triggers it"); "contextmenu should be a PointerEvent");
assert_equals(e.pointerId, pointerdownPointerId,
"contextmenu's pointerId should match the pointerId of the pointer event that triggers it");
assert_equals(e.pointerType, pointerdownPointerType,
"contextmenu's pointerType should match the pointerType of the pointer event that triggers it");
assert_equals(e.composed, true, "contextmenu.composed should be true");
})); }));
target.addEventListener("pointerdown", e=>{ target.addEventListener("pointerdown", e=>{
pointerdownPointerId = e.pointerId; pointerdownPointerId = e.pointerId;

@@ -18,15 +18,18 @@ const input2 = document.querySelector('#input2');
const composedEventTypes = [ const composedEventTypes = [
// UI Events // UI Events
'blur', 'focus', 'focusin', 'focusout', 'blur', 'focus', 'focusin', 'focusout',
'click', 'dblclick', 'dblclick',
'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
'wheel', 'wheel',
'beforeinput', 'input', 'beforeinput', 'input',
'keydown', 'keyup', 'keydown', 'keyup',
'compositionstart', 'compositionupdate', 'compositionend', 'compositionstart', 'compositionupdate', 'compositionend',
// Legacy UI Events // Legacy UI Events
'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', 'keypress', 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', 'keypress',
// See LayoutTests/fast/events/touch/basic-single-touch-events.html for Touch Events. // The following events are covered elsewhere in web_tests:
// - Mouse Events: wpt/uievents/mouse/attributes.html
// - Pointer Events: wpt/pointerevents/pointerevent_*_is_a_pointerevent.html
// and wpt/pointerevents/pointerevent_attributes_*hover*_pointers.html
// - Touch Events: fast/events/touch/basic-single-touch-events.html
]; ];
promise_test( async (t) => { promise_test( async (t) => {
@@ -43,9 +46,6 @@ promise_test( async (t) => {
await mouseClickOn(input.offsetLeft, input.offsetTop); await mouseClickOn(input.offsetLeft, input.offsetTop);
await mouseDoubleClickOn(input.offsetLeft, input.offsetTop); await mouseDoubleClickOn(input.offsetLeft, input.offsetTop);
// For mouseenter/mouseleave
await mouseMoveTo(input2.offsetLeft, input2.offsetTop);
input.blur(); input.blur();
input.focus(); input.focus();
input.blur(); input.blur();