Skip to content

script: pointer events: pointerenter, pointerout, pointerleave, pointerover#42736

Merged
yezhizhen merged 1 commit into
servo:mainfrom
webbeef:pointer-enter-leave-v2
Mar 3, 2026
Merged

script: pointer events: pointerenter, pointerout, pointerleave, pointerover#42736
yezhizhen merged 1 commit into
servo:mainfrom
webbeef:pointer-enter-leave-v2

Conversation

@webbeef
Copy link
Copy Markdown
Contributor

@webbeef webbeef commented Feb 21, 2026

Adds support for more pointer events: pointerenter, pointerout, pointerleave, pointerover
Also add global event handlers that were missing.

Testing: WPT expectations are updated.

cc @yezhizhen

@webbeef webbeef requested a review from gterzian as a code owner February 21, 2026 01:23
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Feb 21, 2026
@yezhizhen yezhizhen self-requested a review February 21, 2026 14:34
@yezhizhen
Copy link
Copy Markdown
Member

I'm reviewing this and will finalize it tomorrow.

Copy link
Copy Markdown
Member

@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do another round for doucument_event_handler.rs.

Comment thread components/script_bindings/webidls/EventHandler.webidl Outdated
Comment thread components/script/dom/touch.rs
let pressure = if event_type == "pointerup" || event_type == "pointercancel" {
// Pressure is 0.5 for active touches, 0.0 for up/cancel/out/leave
let pressure = if event_type == "pointerup" ||
event_type == "pointercancel" ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values of the following properties of the pointercancel event MUST match the values of the last dispatched pointer event with the same pointerId

I don't think this should be 0.

Copy link
Copy Markdown
Member

@yezhizhen yezhizhen Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we can leave this as a follow-up. Just add a TODO. Btw you haven't pushed your new commit yet.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see if I find time to fix that later today :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yezhizhen ok that should be ready now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be covered by any tests tho.

Comment thread components/script/dom/touch.rs
Comment thread components/script/dom/eventtarget.rs
Comment thread components/script/dom/document_event_handler.rs
@webbeef webbeef force-pushed the pointer-enter-leave-v2 branch 3 times, most recently from 2e815ce to 269a42f Compare February 28, 2026 04:51
@yezhizhen yezhizhen added the T-linux-wpt Do a try run of the WPT label Feb 28, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Feb 28, 2026
@github-actions
Copy link
Copy Markdown

🔨 Triggering try run (#22517549670) for Linux (WPT)

@github-actions
Copy link
Copy Markdown

Test results for linux-wpt from try job (#22517549670):

Flaky unexpected result (27)
  • OK [expected TIMEOUT] /IndexedDB/idbfactory_open.any.worker.html
    • PASS [expected FAIL] subtest: Calling open() with version argument 1.5 should not throw.
    • PASS [expected TIMEOUT] subtest: Calling open() with version argument 9007199254740991 should not throw.
    • PASS [expected TIMEOUT] subtest: Calling open() with version argument undefined should not throw.
  • OK /_mozilla/css/offset_properties_inline.html (#40543)
    • FAIL [expected PASS] subtest: offsetTop

      assert_equals: offsetTop of #inline-1 should be 0. expected 0 but got -1
      

    • FAIL [expected PASS] subtest: offsetLeft

      assert_equals: offsetLeft of #inline-2 should be 40. expected 40 but got 25
      

  • ERROR [expected OK] /_mozilla/mozilla/img_load_more_than_cache.html
  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • PASS [expected FAIL] subtest: Delete layer invalidates @font-face
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as <font-family>, not <generic-name>
    • And 12 more unexpected results...
  • TIMEOUT [expected OK] /fetch/api/redirect/redirect-keepalive.https.any.html (#32153)
    • TIMEOUT [expected PASS] subtest: [keepalive][iframe][load] mixed content redirect; setting up

      Test timed out
      

  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/sandboxed-iframe.https.window.html (#41704)
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-site - Not sent to non-trustworthy same-origin destination

      Test timed out
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • PASS [expected FAIL] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • CRASH [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • TIMEOUT [expected FAIL] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      Test timed out
      

  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html (#41193)
    • PASS [expected TIMEOUT] subtest: Default "autoplay" feature policy ["self"] allows same-origin iframes.
  • OK /html/semantics/embedded-content/media-elements/seeking/seek-to-max-value.htm (#40626)
    • FAIL [expected PASS] subtest: seek to Number.MAX_VALUE

      assert_equals: currentTime after setting expected 5.153333333 but got 0
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-video-element/video_timeupdate_on_seek.html
    • PASS [expected TIMEOUT] subtest: timeupdate is emitted after a seek before the data is received: webm.
  • OK /html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)
    • PASS [expected FAIL] subtest: Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.
  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • OK /mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html (#41135)
    • FAIL [expected PASS] subtest: Video of other host autoupgraded

      assert_equals: Length. Other host expected 1 but got Infinity
      

  • OK /pointerevents/compat/pointerevent_touch_target_after_pointerdown_target_removed.tentative.html (#42813)
    • PASS [expected FAIL] subtest: After a touchstart listener moves the target to different position, touch events should be fired on the pointerdown target, but pointer events should be fired on the pointerdown target parent
    • PASS [expected FAIL] subtest: After a touchstart listener moves the target to different position, touchmove event should be fired on the pointerdown target parent
  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • TIMEOUT [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • PASS [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
  • CRASH [expected OK] /webxr/xrSession_sameObject.https.html
Stable unexpected results that are known to be intermittent (20)
  • OK /IndexedDB/transaction-scheduling-mixed-scopes.any.html (#42753)
    • PASS [expected FAIL] subtest: Check that scope restrictions on mixed transactions are enforced.
  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • PASS [expected FAIL] subtest: WebGL test #45
    • PASS [expected FAIL] subtest: WebGL test #47
    • PASS [expected FAIL] subtest: WebGL test #49
    • PASS [expected FAIL] subtest: WebGL test #51
    • FAIL [expected PASS] subtest: WebGL test #53

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #55

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #57

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #59

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • PASS [expected FAIL] subtest: WebGL test #61
    • PASS [expected FAIL] subtest: WebGL test #63
    • And 10 more unexpected results...
  • ERROR [expected CRASH] /_webgl/conformance2/misc/uninitialized-test-2.html (#41656)
  • OK /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = string
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted sans-serif (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-sans-serif (drawing text in a canvas)
  • OK [expected ERROR] /fetch/metadata/window-open.https.sub.html (#40339)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • FAIL [expected PASS] subtest: aElement.click() before the load event must NOT replace

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&code=%0A%20%20%20%20const%20a%20%3D%20document.createElement(%22a%22)%3B%0A%20%20%20%20a.href%20%3D%20%22%2Fcommon%2Fblank.html%3Fthereplacement%22%3B%0A%20%20%20%20document.currentScript.before(a)%3B%0A%20%20%20%20a.click()%3B%0A%20%20"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_3.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 3 but got 2 (expected array [6, 3] got [6, 2])
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
      

  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • FAIL [expected TIMEOUT] subtest: <dialog>-contained autofocus element gets focused when the dialog is shown

      assert_equals: expected "DIV" but got "BODY"
      

  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected PASS] subtest: Non-HTMLElement should not support autofocus

      Test timed out
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • PASS [expected FAIL] subtest: Video should seek to time specified in media fragment syntax
  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • PASS [expected FAIL] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler
  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.worker.html (#33909)
    • FAIL [expected PASS] subtest: Revoking a blob URL immediately after calling import will not fail

      promise_test: Unhandled rejection with value: object "TypeError: Module fetching failed"
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete > Original domComplete
    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd expected true got false
      

    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload fetchStart > Original fetchStart
    • PASS [expected FAIL] subtest: Reload loadEventEnd > Original loadEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventStart > Original loadEventStart
  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

      assert_equals: Number of entries does not match the expected value. expected 3 but got 0
      

  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

@github-actions
Copy link
Copy Markdown

✨ Try run (#22517549670) succeeded.

Copy link
Copy Markdown
Member

@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values of the following properties of the pointercancel event MUST match the values of the last dispatched pointer event with the same pointerId

I don't think this should be 0.

I see that you addressed this in the last commit. But it does not change any test results. Do you know why?

If not, maybe we should just revert the last commit for now, and all the comments below does not matter.

can_gc,
)
} else {
// Fallback: no prior event stored (shouldn't happen in practice)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave a debug_assertion here.

Comment on lines +1125 to +1134
let pe = pointer_touch.to_pointer_event(
window,
pointer_event_name,
pointer_id,
is_primary,
input_event.active_keyboard_modifiers,
event.is_cancelable(),
Some(hit_test_result.point_in_node),
can_gc,
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let pe = pointer_touch.to_pointer_event(
window,
pointer_event_name,
pointer_id,
is_primary,
input_event.active_keyboard_modifiers,
event.is_cancelable(),
Some(hit_test_result.point_in_node),
can_gc,
);

You can inline this.

},
);

pe
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline it here.

let pressure = if event_type == "pointerup" || event_type == "pointercancel" {
// Pressure is 0.5 for active touches, 0.0 for up/cancel/out/leave
let pressure = if event_type == "pointerup" ||
event_type == "pointercancel" ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be covered by any tests tho.

@webbeef webbeef force-pushed the pointer-enter-leave-v2 branch from 269a42f to e7e9ada Compare March 3, 2026 04:35
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Mar 3, 2026
@yezhizhen yezhizhen added this pull request to the merge queue Mar 3, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Mar 3, 2026
Merged via the queue into servo:main with commit 295e019 Mar 3, 2026
30 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Mar 3, 2026
simonwuelker pushed a commit to simonwuelker/servo that referenced this pull request Mar 3, 2026
…erover (servo#42736)

Adds support for more pointer events: pointerenter, pointerout,
pointerleave, pointerover
Also add global event handlers that were missing.

Testing: WPT expectations are updated.

cc @yezhizhen

Signed-off-by: webbeef <[email protected]>
offline-ant pushed a commit to offline-ant/havi that referenced this pull request Jun 4, 2026
…erover (servo#42736)

Adds support for more pointer events: pointerenter, pointerout,
pointerleave, pointerover
Also add global event handlers that were missing.

Testing: WPT expectations are updated.

cc @yezhizhen

Signed-off-by: webbeef <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants