Skip to content

webdriver: Support interspersed pointerMove actions via event queue#42289

Merged
yezhizhen merged 5 commits intoservo:mainfrom
yezhizhen:async-webdriver-move
Feb 3, 2026
Merged

webdriver: Support interspersed pointerMove actions via event queue#42289
yezhizhen merged 5 commits intoservo:mainfrom
yezhizhen:async-webdriver-move

Conversation

@yezhizhen
Copy link
Copy Markdown
Member

@yezhizhen yezhizhen commented Feb 2, 2026

For pointerMove with duration, we need to wait asynchronously and execute in parallel for subsequent moves.
But we never did this correctly: we waited in a blocking way which broke the whole point of tick: making sure we simultaneously perform actions from multiple sources within a single unit time.

To avoid async borrow-checker hell, we achieve the same effect using a event queue.

Spec: The initial pointer movement is performed synchronously. This ensures determinism in the sequence of the first event triggered by each action in the tick.

Subsequent movements (if any) are performed asynchronously. This allows events from two pointerMove actions in the tick to be interspersed.

Testing: Existing WPT tests not affected, which is a good sign. This fixes a potential deadlock: #42289 (comment).
Fixes: #42235
This is a pre-requisite to #41923: multi-touch support.

Signed-off-by: Euclid Ye <[email protected]>
Signed-off-by: Euclid Ye <[email protected]>
Signed-off-by: Euclid Ye <[email protected]>
@yezhizhen yezhizhen requested a review from xiaochengh February 2, 2026 09:35
@yezhizhen yezhizhen changed the title webdriver: Async borrow-checker hell webdriver: Support interspersed pointerMove actions via event queue Feb 2, 2026
@yezhizhen
Copy link
Copy Markdown
Member Author

yezhizhen commented Feb 2, 2026

This is reviewable by commits.

@yezhizhen yezhizhen marked this pull request as ready for review February 2, 2026 10:23
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Feb 2, 2026
@yezhizhen
Copy link
Copy Markdown
Member Author

@yezhizhen yezhizhen added the T-linux-wpt Do a try run of the WPT label Feb 2, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Feb 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 2, 2026

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 2, 2026

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

Flaky unexpected result (39)
  • OK /FileAPI/file/send-file-form-utf-8.html
    • FAIL [expected PASS] subtest: Upload file-for-upload-in-form-.txt (x-user-defined) in UTF-8 form

      assert_greater_than: file-for-upload-in-form-.txt: multipart form data must have at least 3 lines: "" expected a number greater than 2 but got 0
      

  • OK /IndexedDB/idbfactory-databases-opaque-origin.html
    • FAIL [expected PASS] subtest: IDBFactory.databases() in non-sandboxed iframe should not reject

      assert_equals: IDBFactory.databases() should not reject expected "no exception" but got "OperationError"
      

  • OK /IndexedDB/idbfactory-open-error-properties.any.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • OK /IndexedDB/idbfactory-open-error-properties.any.worker.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • ERROR [expected OK] /_mozilla/mozilla/img_find_non_sibling_map.html
  • CRASH [expected OK] /_webgl/conformance/canvas/drawingbuffer-static-canvas-test.html
  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(nastaliq)

      assert_equals: quoted generic(nastaliq) matches  @font-face rule expected 50 but got 30
      

  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • And 12 more unexpected results...
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-rounded (drawing text in a canvas)
  • OK /css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html (#41194)
    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: none; and grid-template-rows: none;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: auto; and grid-template-rows: auto;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: max-content; and grid-template-rows: max-content;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: min-content; and grid-template-rows: min-content;

      assert_in_array: gridTemplateColumns value "28px" not in array ["40px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: auto 1fr; and grid-template-rows: auto 1fr;

      assert_in_array: gridTemplateColumns value "59px 741px" not in array ["90px 710px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: min-content 1fr calc(20px + 10%) minmax(30em, 50em); and grid-template-rows: min-content 1fr calc(10% + 40px) minmax(3em, 5em);

      assert_in_array: gridTemplateColumns value "28px 172px 100px 500px" not in array ["40px 160px 100px 500px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: foo; and grid-template-rows: bar;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: auto none; and grid-template-rows: none auto;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: 100px, 200px; and grid-template-rows: 300px, 400px;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: minmax(100px, 200px, 300px); and grid-template-rows: minmax(100px, 200px, 300px);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

  • CRASH [expected PASS] /css/css-overflow/scroll-marker-in-display-none-column-crash.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
    • PASS [expected FAIL] subtest: setFormValue with an empty FormData should submit nothing
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/sandboxed-iframe.https.window.html (#41704)
  • CRASH [expected OK] /html/browsers/history/the-history-interface/combination_history_006.html
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected OK] /html/browsers/the-window-object/window-open-noopener.html?_top
  • 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/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • PASS [expected FAIL] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe
  • 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 /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: Basic File test (normal form)

      assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"; filename=\"file-test.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n--\r\n" but got ""
      

    • PASS [expected FAIL] subtest: multipart/form-data: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in value (formdata event)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: Basic test (formdata event)

      assert_equals: expected "basic=test\r\n" but got ""
      

    • FAIL [expected PASS] subtest: text/plain: Basic File test (normal form)

      assert_equals: expected "basic=file-test.txt\r\n" but got ""
      

    • PASS [expected FAIL] subtest: text/plain: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in value (formdata event)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (formdata event)

      assert_equals: expected "basic=test" but got ""
      

    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in value (formdata event)
  • TIMEOUT [expected ERROR] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html (#40347)
  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • FAIL [expected PASS] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler

      assert_array_equals: expected property 1 to be "Script #1 ran" but got "Script #3 ran" (expected array ["Script #2 ran", "Script #1 ran", "Script #3 ran", "Script #4 ran"] got ["Script #2 ran", "Script #3 ran", "Script #4 ran", "Script #1 ran"])
      

  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.html (#33948)
    • 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 [expected ERROR] /html/user-activation/no-activation-thru-escape-key.html (#40343)
  • CRASH [expected OK] /html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html
  • PASS [expected FAIL] /png/apng/fcTL-dispose-none.html (#41817)
  • FAIL [expected PASS] /png/apng/fcTL-dispose-previous-final.html (#41837)
  • FAIL [expected PASS] /png/apng/fcTL-dispose-previous-first.html
  • OK /preload/link-header-preload-delay-onload.html (#39622)
    • FAIL [expected PASS] subtest: Makes sure that Link headers preload resources and block window.onload after resource discovery

      assert_true: expected true got false
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • TIMEOUT [expected PASS] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via anchor with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in enforcing mode.
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy throwing an exception in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy making the URL invalid in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

  • OK /webdriver/tests/classic/element_click/bubbling.py
    • FAIL [expected PASS] subtest: test_click_event_bubbles_to_parents

      webdriver.error.NoSuchWindowException: no such window (404): No such window
      

  • OK /webdriver/tests/classic/element_click/interactability.py
    • FAIL [expected PASS] subtest: test_display_none

      webdriver.error.NoSuchWindowException: no such window (404): No such window
      

  • OK /webdriver/tests/classic/get_element_rect/get.py
    • ERROR [expected PASS] subtest: test_no_top_browsing_context

      setup error: webdriver.error.NoSuchElementException: no such element (404)
      

  • CRASH [expected OK] /workers/WorkerGlobalScope_importScripts.htm
Stable unexpected results that are known to be intermittent (27)
  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.html (#38895)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.worker.html (#38971)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/key-conversion-exceptions.any.html (#39305)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() =&gt; {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() =&gt; {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • OK /IndexedDB/key-conversion-exceptions.any.worker.html (#39284)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() =&gt; {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() =&gt; {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • 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 /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "38.3166666666667px"
      

  • OK /css/cssom-view/offsetTopLeft-border-box.html (#40826)
    • PASS [expected FAIL] subtest: container: 0
    • PASS [expected FAIL] subtest: container: 1
  • 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
      

  • ERROR [expected OK] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • FAIL [expected PASS] subtest: sec-fetch-mode

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • FAIL [expected PASS] subtest: sec-fetch-user

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • PASS [expected FAIL] subtest: load event does not fire on window.open('about:blank')
  • 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)&amp;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"
      

  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html (#41404)
    • TIMEOUT [expected PASS] subtest: Feature-Policy header: autoplay * allows same-origin iframes.

      Test timed out
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html (#41221)
    • PASS [expected TIMEOUT] subtest: Feature-Policy header: autoplay "none" has no effect on the top level document.
  • TIMEOUT /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • PASS [expected TIMEOUT] subtest: Speed-ups should not change the pitch when preservesPitch=true
    • PASS [expected NOTRUN] subtest: Slow-downs should not change the pitch when preservesPitch=true
    • FAIL [expected NOTRUN] subtest: Speed-ups should change the pitch when preservesPitch=false

      assert_approx_equals: The actual pitch should be close to the expected pitch. expected 880 +/- 132 but got 431.0850439882698
      

    • TIMEOUT [expected NOTRUN] subtest: Slow-downs should change the pitch when preservesPitch=false

      Test timed out
      

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

      assert_true: Reload domComplete &gt; Original domComplete expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventEnd &gt; Original loadEventEnd

      assert_true: Reload loadEventEnd &gt; Original loadEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventStart &gt; Original loadEventStart

      assert_true: Reload loadEventStart &gt; Original loadEventStart expected true got false
      

  • OK [expected CRASH] /resource-timing/render-blocking-status-link.html (#41664)
  • OK /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
    • FAIL [expected NOTRUN] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)

      assert_equals: expected 6.650000000000002 but got 6.64
      

  • OK /resource-timing/test_resource_timing.https.html (#25216)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)

      assert_equals: expected 8.549999999999997 but got 8.55
      

  • CRASH [expected ERROR] /webxr/render_state_update.https.html (#27535)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 2, 2026

✨ Try run (#21586343788) succeeded.

@yezhizhen yezhizhen force-pushed the async-webdriver-move branch from f741752 to 781db23 Compare February 2, 2026 12:23
Signed-off-by: Euclid Ye <[email protected]>
Copy link
Copy Markdown
Member

@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

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

Seems reasonable.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Feb 2, 2026
@yezhizhen
Copy link
Copy Markdown
Member Author

Let me try to add a test.

@yezhizhen
Copy link
Copy Markdown
Member Author

yezhizhen commented Feb 3, 2026

def test_interspersed_touch_moves(session, test_actions_pointer_page):
    pointerArea = session.find.css("#pointerArea", all=False)

    session.execute_script("""
        window.touchmoveEvents = [];
        const area = document.getElementById("pointerArea");
        area.addEventListener("touchmove", (event) => {
            window.touchmoveEvents.push({
                "type": "touchmove",
                "pointerId": event.changedTouches[0].identifier,
                "pageX": event.changedtouches[[0].pageX,
                "pageY": event.changedtouches[[0].pageY,
            });
        });
    """)
    
    # Two fingers
    touch_chain_1 = session.actions.sequence(
        "pointer",
        "touch_pointer_1",
        {"pointerType": "touch"})
    touch_chain_2 = session.actions.sequence(
        "pointer",
        "touch_pointer_2",
        {"pointerType": "touch"})

    touch_chain_1 \
        .pointer_move(0, 0, origin=pointerArea) \
        .pointer_down() \
        .pointer_move(10, 10, origin=pointerArea, duration=200) \
        .pointer_up()

    touch_chain_2 \
        .pointer_move(1, 1, origin=pointerArea) \
        .pointer_down() \
        .pointer_move(20, 20, origin=pointerArea, duration=200) \
        .pointer_up()

    # Perform both action sequences in parallel.
    session.actions.perform([touch_chain_1.dict, touch_chain_2.dict])

    
    time.sleep(2)
    touchmove_events = session.execute_script("return window.touchmoveEvents;")

    assert len(touchmove_events) > 0, "No touchmove events recorded"

    # Verify events are interspersed: alternating patterns
    pointer_ids = [e["pointerId"] for e in touchmove_events]
   
    unique_pointer_ids = set(pointer_ids)
    assert len(unique_pointer_ids) == 2, \
        f"Expected touchmove events from 2 pointers, but got from {len(unique_pointer_ids)}"

I added a local test, but I'm reluctant to push this upstream. But I see clear improvement from this PR:
the test would have deadlock before the PR.

Now I can see pinch zoom effect and test completion to the end.

@yezhizhen yezhizhen added this pull request to the merge queue Feb 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 Feb 3, 2026
Merged via the queue into servo:main with commit aedbebb Feb 3, 2026
34 checks passed
@yezhizhen yezhizhen deleted the async-webdriver-move branch February 3, 2026 05:00
@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 Feb 3, 2026
github-merge-queue bot pushed a commit that referenced this pull request Mar 11, 2026
Scroll and PointerMove is quite similar. We did so for `PointeMove` in
#42289 +
#42946.

Imagine having 3 non-zero duration scroll actions with origin at
different Elements.
Previously we would wait for one to be fully dispatched before moving to
next action,
instead of interspersed.

We also consolidate `PendingPointerMove` and
newly added `PendingScroll` into `enum PendingActions`.

Testing: 
[Existing tests behaviour not
changing.](https://github.com/servo/servo/actions/runs/22887005716).
Added a wdspec test.
Previously: `['wheel', 'wheel', 'wheel', 'wheel', 'wheel', 'wheel',
'wheel', 'wheel', 'wheel', 'move']`
Now: `['wheel', 'move', 'wheel', 'move', 'wheel', 'move', 'move',
'wheel', 'wheel', 'move', 'move', 'wheel', 'wheel', 'move', 'move',
'wheel']`

---------

Signed-off-by: Euclid Ye <[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.

PointerMove: Should not wait synchronously for all subsequent pointer moves

3 participants