Skip to content

Conversation

@tharkum
Copy link
Contributor

@tharkum tharkum commented Nov 17, 2025

The user agent on the internal play steps (step 2) must to seek to the earliest possible position if the playback has ended, but the definition of the ended playback prevents looping when the loop attribute was added after playback has ended (already registered whatwg html issue).

- playback ended (`ended` event)
- loop = true
- play() (no seeking - playback hasn't ended)

Currently this edge case if not yet reflected in the HTML specification so let's do the same as other browsers and ignore the loop attribute for play seek.

See https://html.spec.whatwg.org/multipage/#internal-play-steps
See whatwg/html#4487

Testing: Improvements in the following tests

  • html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html

@tharkum tharkum requested a review from gterzian as a code owner November 17, 2025 11:20
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 17, 2025
@tharkum tharkum requested review from TimvdLippe and jdm and removed request for gterzian and jdm November 17, 2025 11:20
@tharkum tharkum added the T-linux-wpt Do a try run of the WPT label Nov 17, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 17, 2025
@github-actions
Copy link

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

@tharkum
Copy link
Contributor Author

tharkum commented Nov 17, 2025

Please notice that this pull request is focusing on the play seek and ended_playback specification issue, while there are existing intermittent issue for associated WPT test, and it is related to the timing issue (slow event marshaling between the script runtime and the media engine).

So in summary this pull request is changing expected test result from TIMEOUT to OK, but not fixing the intermittent timing issue.

@github-actions
Copy link

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

Flaky unexpected result (41)
  • OK /IndexedDB/idbfactory_open.any.html
    • FAIL [expected PASS] subtest: Calling open() with version argument 1.5 should not throw.

      assert_equals: version expected 1 but got 9007199254740991
      

  • 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
      

  • OK /_mozilla/mozilla/getBoundingClientRect.html (#39668)
    • FAIL [expected PASS] subtest: getBoundingClientRect 1

      assert_equals: expected 62 but got 60.35
      

  • OK /_mozilla/webxr/create_session.https.html
    • FAIL [expected PASS] subtest: create_session

      can't access property "simulateDeviceConnection", navigator.xr.test is undefined
      

  • CRASH [expected OK] /_webgl/conformance/ogles/GL/lessThanEqual/lessThanEqual_001_to_008.html
  • CRASH [expected OK] /_webgl/conformance/ogles/GL/swizzlers/swizzlers_033_to_040.html
  • CRASH [expected OK] /_webgl/conformance/programs/program-test.html
  • CRASH [expected OK] /_webgl/conformance/rendering/gl-scissor-test.html
  • CRASH [expected OK] /_webgl/conformance/rendering/many-draw-calls.html
  • CRASH [expected OK] /_webgl/conformance/textures/image/tex-2d-alpha-alpha-unsigned_byte.html
  • CRASH [expected ERROR] /_webgl/conformance/textures/misc/texture-srgb-upload.html
  • CRASH [expected OK] /credential-management/idlharness.https.window.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: setFormValue with an empty FormData should submit nothing
  • 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 /fetch/fetch-later/new-window.https.window.html (#32036)
    • FAIL [expected PASS] subtest: A same-origin window[target=''][features=''] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target=''][features='popup'] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features=''] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features='popup'] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

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

  • OK /html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • FAIL [expected PASS] subtest: Same-Document Referrer from Refresh

      assert_equals: original page loads expected "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section#section"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected OK] /html/browsers/the-window-object/window-open-noreferrer.html
  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • FAIL [expected PASS] subtest: opener of discarded auxiliary browsing context

      assert_object_equals: property "get" expected function "function opener() {
          [native code]
      }" got function "function opener() {
          [native code]
      }"
      

  • CRASH [expected OK] /html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.negative.html
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • FAIL [expected PASS] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe

      uncaught exception: Error: assert_unreached: The iframe into which the meta was moved must not refresh Reached unreachable code
      

  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • FAIL [expected PASS] subtest: Video should seek to time specified in media fragment syntax

      assert_equals: expected 3 but got 0
      

  • 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.
  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in name (formdata event)
    • PASS [expected FAIL] subtest: multipart/form-data: single quote in value (formdata event)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • 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)
    • PASS [expected FAIL] subtest: text/plain: single quote in filename (formdata event)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic File test (normal form)

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

    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic File test (formdata event)
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • TIMEOUT [expected ERROR] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html (#40347)
  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • FAIL [expected PASS] subtest: print() during unload

      assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
      

  • PASS [expected FAIL] /png/apng/fcTL-dispose-none.html
  • CRASH [expected OK] /pointerevents/pointerevent_multiple_pointerover_no_pointer_movement.html
  • ERROR [expected OK] /resource-timing/cors-preflight.any.html (#28694)
  • CRASH [expected OK] /trusted-types/Element-setAttribute-setAttributeNS-sinks.tentative.html
  • 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.
  • CRASH [expected TIMEOUT] /uievents/mouse/cancel-mousedown-in-subframe.html
  • CRASH [expected TIMEOUT] /wasm/webapi/empty-body.any.worker.html
  • 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
  • TIMEOUT [expected OK] /workers/nested_worker_close_from_parent_worker.html
  • OK /xhr/send-redirect.htm (#32026)
    • FAIL [expected PASS] subtest: XMLHttpRequest: send() - Redirects (basics) (303, GET, content.py)

      assert_equals: expected (string) "GET" but got (object) null
      

Stable unexpected results that are known to be intermittent (24)
  • 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/transaction-deactivation-timing.any.html (#38772)
    • PASS [expected FAIL] subtest: New transactions are deactivated before next task
  • 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-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai)
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-rounded (drawing text in a canvas)
  • OK /fetch/api/credentials/cookies.any.html (#40672)
    • FAIL [expected PASS] subtest: Include mode: 1 cookie

      assert_equals: Request include cookie(s) expected "a=1" but got "a=1; domain-attribute-matches-host=0"
      

    • FAIL [expected PASS] subtest: Include mode: 2 cookies

      assert_equals: Request include cookie(s) expected "b=2; c=3" but got "b=2; c=3; domain-attribute-matches-host=0"
      

    • FAIL [expected PASS] subtest: Omit mode: no cookie is stored

      assert_false: Request does not have cookie(s) expected false got true
      

    • FAIL [expected PASS] subtest: Same-origin mode: 1 cookie

      assert_equals: Request include cookie(s) expected "a=1" but got "a=1; domain-attribute-matches-host=0"
      

    • FAIL [expected PASS] subtest: Same-origin mode: 2 cookies

      assert_equals: Request include cookie(s) expected "b=2; c=3" but got "b=2; c=3; domain-attribute-matches-host=0"
      

  • OK /fetch/fetch-later/permissions-policy/deferred-fetch-allowed-by-permissions-policy.https.window.html (#40478)
    • FAIL [expected PASS] subtest: Permissions policy header: "deferred-fetch=*" allows fetchLater() in the top-level document.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-user
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Cross-site

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

  • OK /fetch/metadata/generated/element-img-environment-change.https.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes

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

    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -> Same Origin, no attributes

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

    • PASS [expected FAIL] subtest: sec-fetch-site - Same-Origin -> Same-Site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -> Cross-Site, no attributes

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

    • PASS [expected FAIL] subtest: sec-fetch-site - Same-Site -> Cross-Site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-mode - attributes: crossorigin=use-credentials

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

  • OK /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes

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

    • PASS [expected FAIL] subtest: sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes
  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • FAIL [expected PASS] subtest: Link with onclick navigation and href navigation

      assert_equals: expected "href" but got "click"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored

      promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • PASS [expected FAIL] subtest: aElement.click() before the load event must NOT replace
  • ERROR [expected OK] /html/infrastructure/common-dom-interfaces/collections/domstringlist.html (#40665)
  • 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"
      

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

      assert_equals: expected "cannot navigate" but got "can navigate"
      

  • CRASH [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#40418)
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: 404 (fetch): main
    • PASS [expected FAIL] subtest: CORS (fetch): main
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected FAIL] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected FAIL] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • 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
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
  • ERROR [expected OK] /workers/baseurl/alpha/import-in-moduleworker.html (#21315)

@github-actions
Copy link

✨ Try run (#19427878425) succeeded.

Copy link
Contributor

@TimvdLippe TimvdLippe left a comment

Choose a reason for hiding this comment

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

1 nit about the boolean, the rest LGTM. After merging, can you monitor the intermittent issue and see if it remains intermittent after 7 days? That way we can know if it is resolved or not.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 17, 2025
@tharkum
Copy link
Contributor Author

tharkum commented Nov 17, 2025

1 nit about the boolean, the rest LGTM. After merging, can you monitor the intermittent issue and see if it remains intermittent after 7 days? That way we can know if it is resolved or not.

It will not be resolved by this PR - #33778 (comment)

…layback ended

The user agent on the `internal play steps` (step 2) must
to seek to the earliest possible position if the playback
has ended, but the definition of the `ended playback`
prevents looping when the loop attribute was added after
playback has ended (already registered whatwg html issue).
```
- playback ended (`ended` event)
- loop = true
- play() (no seeking - playback hasn't ended)
```

Currently this edge case if not yet reflected in the HTML specification
so let's do the same as other browsers and ignore the `loop`
attribute for `play` seek.

See https://html.spec.whatwg.org/multipage/#internal-play-steps
See whatwg/html#4487

Testing: Improvements in the following tests
- html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html

Signed-off-by: Andrei Volykhin <andrei.volykhin@gmail.com>
@tharkum tharkum force-pushed the html-media-play-seek-ignore-loop-attribute branch from 822a7d6 to dc3815a Compare November 17, 2025 13:21
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 17, 2025
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 17, 2025
@TimvdLippe TimvdLippe added this pull request to the merge queue Nov 17, 2025
@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 Nov 17, 2025
Merged via the queue into servo:main with commit 1301dbf Nov 17, 2025
35 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 Nov 17, 2025
@tharkum tharkum deleted the html-media-play-seek-ignore-loop-attribute branch November 17, 2025 16:50
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