Skip to content

htmlmediaelement: Fix fetch request race on "seek-data" event#37002

Merged
jdm merged 1 commit intoservo:mainfrom
tharkum:htmlmediaelement-fetch-request-race-on-seek-event
Jun 2, 2025
Merged

htmlmediaelement: Fix fetch request race on "seek-data" event#37002
jdm merged 1 commit intoservo:mainfrom
tharkum:htmlmediaelement-fetch-request-race-on-seek-event

Conversation

@tharkum
Copy link
Copy Markdown
Contributor

@tharkum tharkum commented May 14, 2025

On execution media element load algorithm
https://html.spec.whatwg.org/multipage/media.html#media-element-load-algorithm
we can observe race condition between parallel running fetch requests:
R1: (media element) initial request (see "resource_fetch_algorithm" function)
R2: (gstreamer) duration "seek-data" request (see PlayerEvent::SeekData)
R3: (gstreamer) continue on last interrupted time "seek-data" request

At time there are only one current fetch context for media element but
because resource fetch and cancellation are async operations need
to identify (by request id) and skip processing outdated fetch request
(fetch listener callbacks).

Async load in background sequence (stream content length = 2757913):

R1 (seek-offset=0):          [------------------------------X]
R2 (seek-offset=2757866):          [---------Y]
                               AS-****-+++-AE
R3 (seek-offset=98304):                         [----------------Z]
                                             BS-****-+++BE

R1 (seek-offset=0):          [------X]
R2 (seek-offset=2757866):                [--------Y] (discarded data)
                                     AS-****---------+++-AE

- A*/B* performed seeks (*** seek lock, +++ flush buffer queue + reset EOS)
  (on Gstreamer streaming thread)
- X/Y/Z "end-of-stream" events from fetch requests
  (on Servo script thread)

All incoming input data from different requests are mixed and
pushed to active media player, plus abnormal behaviour due to
intermixed X/Y/Z "end-of-stream" events.

To handle it properly we will unblock seek lock on "seek-data" event
(on script thread) as soon as possible (GStreamer will be able
to flush buffer queue and reset EOS state) and introduce buffered data source
to delay pushing input data/EOS event until media player will be actually ready.

Testing: Improvements to the following tests:

  • /html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
  • /html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html

Fixes: #31931
Fixes: #36989

@tharkum tharkum requested a review from gterzian as a code owner May 14, 2025 14:03
@tharkum
Copy link
Copy Markdown
Contributor Author

tharkum commented May 14, 2025

NOTE: This PR is partial fix - sometimes GStreamer doesn't want to continue do second "seek" or set "paused" state (due it no load event).

@jdm < Could you please run linux WPT?

@jdm jdm added the T-linux-wpt Do a try run of the WPT label May 14, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label May 14, 2025
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (24)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.

      Test timed out
      

  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resizeTo.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK [expected TIMEOUT] /_webgl/conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html
    • PASS [expected NOTRUN] subtest: Overall test
    • FAIL [expected PASS] subtest: WebGL test #1

      assert_true: createImageBitmap(source) failed: "argument could not be converted to any of: HTMLImageElement, HTMLCanvasElement, OffscreenCanvas, CSSStyleValue, Blob, ImageData" expected true got false
      

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

      assert_true: createImageBitmap(source) failed: "argument could not be converted to any of: HTMLImageElement, HTMLCanvasElement, OffscreenCanvas, CSSStyleValue, Blob, ImageData" expected true got false
      

  • OK /css/css-values/cap-invalidation.html (#32757)
    • FAIL [expected PASS] subtest: CSS Values and Units Test: cap invalidation

      uncaught exception: Error: assert_not_equals: expect the capital height of Ahem and sans-serif to be different got disallowed value 371.3333333333333
      

  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.tentative.https.window.html (#35176)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
    • PASS [expected FAIL] subtest: sec-fetch-user
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/008.html (#24456)
    • PASS [expected FAIL] subtest: Link with onclick form submit to javascript url and href navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/009.html (#24456)
    • FAIL [expected PASS] subtest: Link with onclick form submit to javascript url with document.write and href navigation

      assert_array_equals: expected property 1 to be "href" but got "click" (expected array ["write", "href"] got ["write", "click"])
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • PASS [expected FAIL] subtest: Cross-origin navigation started from unload handler must be ignored
  • 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
  • TIMEOUT [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html (#28716)
    • TIMEOUT [expected FAIL] subtest: Replace before load, triggered by formElement.requestSubmit()

      Test timed out
      

  • OK /html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html (#29181)
    • PASS [expected FAIL] subtest: cross-document navigation then cross-document navigation
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • ERROR [expected OK] /html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html (#34119)
  • CRASH [expected TIMEOUT] /html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage.https.html
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • TIMEOUT [expected FAIL] subtest: &lt;dialog&gt;-contained autofocus element gets focused when the dialog is shown

      Test timed out
      

  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html (#28259)
    • TIMEOUT [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with "top" fragments should work.

      Test timed out
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-reload-location-reload.html (#32595)
    • FAIL [expected PASS] subtest: Reloading iframe loading='lazy' before it is loaded: location.reload

      uncaught exception: Error: assert_equals: expected "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?src" but got "about:blank"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html (#24057)
  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html (#24066)
    • NOTRUN [expected FAIL] subtest: Check that popups from a sandboxed iframe do not escape the sandbox
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • PASS [expected FAIL] subtest: text/plain: Basic File test (normal form)
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected PASS] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

Stable unexpected results that are known to be intermittent (11)
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #44

      assert_true: could not create image (SVG) expected true got false
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html (#34819)
    • FAIL [expected PASS] subtest: form submission

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1=" but got "about:blank"
      

  • 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
  • TIMEOUT /html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html (#31931)
    • TIMEOUT [expected FAIL] subtest: redirected to cross-origin HTMLVideoElement: origin unclear getImageData

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: redirected to cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage
    • NOTRUN [expected FAIL] subtest: redirected to cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap
    • NOTRUN [expected FAIL] subtest: redirected to same-origin HTMLVideoElement: origin unclear getImageData
    • NOTRUN [expected FAIL] subtest: redirected to same-origin HTMLVideoElement: origin unclear 2dContext.drawImage
    • NOTRUN [expected FAIL] subtest: redirected to same-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap
    • NOTRUN [expected FAIL] subtest: unclean HTMLCanvasElement: origin unclear getImageData
    • NOTRUN [expected FAIL] subtest: unclean HTMLCanvasElement: origin unclear 2dContext.drawImage
    • NOTRUN [expected FAIL] subtest: unclean HTMLCanvasElement: origin unclear bitmaprenderer.transferFromImageBitmap
    • NOTRUN [expected FAIL] subtest: unclean ImageBitmap: origin unclear getImageData
    • And 2 more unexpected results...
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node &lt;div autofocus=""&gt;&lt;/div&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus including no focusable descendants should be skipped

      assert_equals: expected Element node &lt;input autofocus=""&gt;&lt;/input&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;input autofocus=""&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Area element should support autofocus

      assert_equals: expected Element node &lt;area autofocus="" href="/common/blank.html"&gt;&lt;/area&gt; but got Element node &lt;body&gt;
      &lt;img src="/media/poster.png" usemap="#map"&gt;
      &lt;map n...
      

  • TIMEOUT /html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html (#36989)
    • FAIL [expected TIMEOUT] subtest: redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean

      promise_test: Unhandled rejection with value: object "TypeError: argument could not be converted to any of: HTMLImageElement, HTMLCanvasElement, OffscreenCanvas, CSSStyleValue"
      

    • PASS [expected NOTRUN] subtest: unclean HTMLCanvasElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean
    • FAIL [expected NOTRUN] subtest: unclean ImageBitmap: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean

      promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
      

    • PASS [expected NOTRUN] subtest: cross-origin HTMLImageElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean
    • FAIL [expected NOTRUN] subtest: cross-origin SVGImageElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean

      promise_test: Unhandled rejection with value: object "[object Event]"
      

    • TIMEOUT [expected NOTRUN] subtest: cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean

      Test timed out
      

  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
  • OK /html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm (#34434)
    • FAIL [expected PASS] subtest: default object size after src is removed

      assert_equals: expected "320px" but got "300px"
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete &gt; Original domComplete
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}. Index Actual Expected AbsError RelError Test threshold [14650] -8.6445595950713842e-32 8.6956524848937988e-1 8.6956524848937988e-1 1.0000000000000000e+0 3.8985999999999999e-3 [14651] 3.0547976493835449e-1 8.9879405498504639e-1 5.9331429004669189e-1 6.6012262403823208e-1 3.8985999999999999e-3 Max AbsError of 8.6956524848937988e-1 at index of 14650. Max RelError of 1.0000000000000000e+0 at index of 14650.

      assert_true: expected true got false
      

  • ERROR [expected OK] /workers/constructors/Worker/Worker-constructor.html (#22991)

@github-actions
Copy link
Copy Markdown

✨ Try run (#15022924730) succeeded.

@tharkum tharkum force-pushed the htmlmediaelement-fetch-request-race-on-seek-event branch from 205114a to d55941a Compare May 16, 2025 12:27
@tharkum tharkum changed the title htmlmediaelement: Fix fetch request race on player "seek" event htmlmediaelement: Fix fetch request race on "seek-data" event May 16, 2025
@tharkum
Copy link
Copy Markdown
Contributor Author

tharkum commented May 16, 2025

@jdm < I found and fixed additional issue with "seek lock".
Please run linux WPT. Thanks

@jdm jdm added the T-linux-wpt Do a try run of the WPT label May 16, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label May 16, 2025
@github-actions
Copy link
Copy Markdown

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

@jdm
Copy link
Copy Markdown
Member

jdm commented May 16, 2025

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (16)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resizeTo.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/out-of-bounds-uniform-array-access.html (#26225)
    • NOTRUN [expected PASS] subtest: Overall test
  • FAIL [expected PASS] /css/css-fonts/font-palette-24.html
  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • FAIL [expected PASS] subtest: Matching font-style: 'italic' should prefer 'italic' over 'oblique 20deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 0deg' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
    • PASS [expected FAIL] subtest: sec-fetch-user
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • FAIL [expected PASS] subtest: Check execution order from nested timeout

      assert_equals: Expected nested setTimeout to run second expected true but got false
      

    • FAIL [expected PASS] subtest: Check execution order on load handler

      assert_equals: Expected onload to run first expected false but got true
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html (#28066)
    • FAIL [expected PASS] subtest: 0041 set in href="" targeting a frame and clicked

      assert_equals: expected "A" but got ""
      

    • FAIL [expected PASS] subtest: 0080 00FF set in href="" targeting a frame and clicked

      assert_equals: expected "�ÿ" but got ""
      

    • FAIL [expected PASS] subtest: 0080 00FF 0100 set in href="" targeting a frame and clicked

      assert_equals: expected "�ÿĀ" but got ""
      

    • FAIL [expected PASS] subtest: D83D DE0D set in href="" targeting a frame and clicked

      assert_equals: expected "😍" but got ""
      

    • FAIL [expected PASS] subtest: DE0D 0041 set in href="" targeting a frame and clicked

      assert_equals: expected "\ufffdA" but got ""
      

  • TIMEOUT [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html (#28716)
    • TIMEOUT [expected FAIL] subtest: Replace before load, triggered by formElement.requestSubmit()

      Test timed out
      

  • OK /html/browsers/windows/browsing-context-names/duplicate-name-order.html (#34623)
    • PASS [expected FAIL] subtest: Duplicate name lookup order
  • 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
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
    • FAIL [expected NOTRUN] subtest: Check that popups from a sandboxed iframe do not escape the sandbox

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • 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"])
      

  • ERROR /service-workers/idlharness.https.any.html (#36250)
    • PASS [expected TIMEOUT] subtest: ServiceWorkerContainer interface: operation register((TrustedScriptURL or USVString), optional RegistrationOptions)
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation enable()
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation disable()
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation setHeaderValue(ByteString)
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation getState()
  • TIMEOUT [expected OK] /service-workers/service-worker/detached-context.https.html
    • TIMEOUT [expected FAIL] subtest: accessing navigator.serviceWorker on a detached iframe

      Test timed out
      

Stable unexpected results that are known to be intermittent (10)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.

      Test timed out
      

  • FAIL [expected PASS] /_mozilla/css/dirty_viewport.html (#13731)
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #44

      assert_true: could not create image (SVG) expected true got false
      

  • PASS [expected FAIL] /css/compositing/mix-blend-mode/mix-blend-mode-video-sibling.html (#32849)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html (#29048)
    • FAIL [expected PASS] subtest: Navigating to a different document with link click

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1" but got "about:blank"
      

    • FAIL [expected PASS] subtest: Navigating to a different document with form submission

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1=" but got "about:blank"
      

  • ERROR [expected OK] /html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html (#34120)
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node &lt;div autofocus=""&gt;&lt;/div&gt; but got Element node &lt;body&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus including no focusable descendants should be skipped

      assert_equals: expected Element node &lt;input autofocus=""&gt;&lt;/input&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;input autofocus=""&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Area element should support autofocus

      promise_test: Unhandled rejection with value: object "TypeError: w.document.querySelector(...) is null"
      

  • OK /html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm (#34434)
    • FAIL [expected PASS] subtest: default object size after src is removed

      assert_equals: expected "320px" but got "300px"
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete &gt; Original domComplete
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart &gt; Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload domInteractive &gt; Original domInteractive
    • PASS [expected FAIL] subtest: Reload fetchStart &gt; Original fetchStart
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}. Index Actual Expected AbsError RelError Test threshold [14650] -7.5279974400000000e+9 8.6956524848937988e-1 7.5279974408695650e+9 8.6571967473945179e+9 3.8985999999999999e-3 [14651] 3.0547976493835449e-1 8.9879405498504639e-1 5.9331429004669189e-1 6.6012262403823208e-1 3.8985999999999999e-3 Max AbsError of 7.5279974408695650e+9 at index of 14650. Max RelError of 8.6571967473945179e+9 at index of 14650.

      assert_true: expected true got false
      

    • FAIL [expected PASS] subtest: X SNR (-154.09950331158657 dB) is not greater than or equal to 65.737. Got -154.09950331158657.

      assert_true: expected true got false
      

@github-actions
Copy link
Copy Markdown

✨ Try run (#15068747839) succeeded.

@tharkum
Copy link
Copy Markdown
Contributor Author

tharkum commented May 16, 2025

cc @xiaochengh < FYI

@jdm < Do you know any media expert from servo team who can review PR?

@jdm
Copy link
Copy Markdown
Member

jdm commented May 16, 2025

I'll take a look tonight.

Edit: sorry, not enough brain power tonight.

@xiaochengh
Copy link
Copy Markdown
Contributor

cc @rayguo17

Copy link
Copy Markdown
Member

@jdm jdm left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, but I really needed time to sit down and think about these changes. I'm vaguely concerned about the increase in complexity that's spreading through different methods, but I don't have any concrete suggestions on how to reduce it right now. I feel confident that these changes are addressing real issues in the current implementation, so let's get them merged!

On execution media element load algorithm
https://html.spec.whatwg.org/multipage/media.html#media-element-load-algorithm
we can observe race condition between parallel running fetch requests:
 R1: (media element) initial request (see "resource_fetch_algorithm" function)
 R2: (gstreamer) duration "seek-data" request (see PlayerEvent::SeekData)
 R3: (gstreamer) continue on last interrupted time "seek-data" request

At time there are only one current fetch context for media element but
because resource fetch and cancellation are async operations need
to identify (by request id) and skip processing outdated fetch request
(fetch listener callbacks).

Async load in background sequence (stream content length = 2757913):
R1 (seek-offset=0):          [------------------------------X]
R2 (seek-offset=2757866):          [---------Y]
                               AS-****-+++-AE
R3 (seek-offset=98304):                         [----------------Z]
                                             BS-****-+++BE

R1 (seek-offset=0):          [------X]
R2 (seek-offset=2757866):                [--------Y] (discarded data)
                                     AS-****---------+++-AE

- A*/B* performed seeks (*** seek lock, +++ flush buffer queue + reset EOS)
  (on Gstreamer streaming thread)
- X/Y/Z "end-of-stream" events from fetch requests
  (on Servo script thread)

All incoming input data from different requests are mixed and
pushed to active media player, plus abnormal behaviour due to
intermixed X/Y/Z "end-of-stream" events.

To handle it properly we will unblock seek lock on "seek-data" event
(on script thread) as soon as possible (GStreamer will be able
to flush buffer queue and reset EOS state) and introduce buffered data source
to delay pushing input data/EOS event until media player will be actually ready.

Testing: Improvements to the following tests:
  - /html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
  - /html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html

Fixes: servo#31931
Fixes: servo#36989

Signed-off-by: Andrei Volykhin <[email protected]>
@tharkum tharkum force-pushed the htmlmediaelement-fetch-request-race-on-seek-event branch from d55941a to c678f81 Compare June 2, 2025 13:55
@tharkum
Copy link
Copy Markdown
Contributor Author

tharkum commented Jun 2, 2025

@jdm < Sorry for long delay. After your comments I slightly refactored the original code and introduced the BufferedDataSource to decouple all messy "payload data/EOS" logic from FetchContext.

In future I am planning to add required seek functionlity for data:// URL (#32645) into BufferedDataSource as well (or may be convert in into enum DataSource (Memory, Buffered)

Please take a look and share you feedback.

Copy link
Copy Markdown
Member

@jdm jdm left a comment

Choose a reason for hiding this comment

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

I like the changes here!


fn handle_player_event(&self, event: &PlayerEvent, can_gc: CanGc) {
fn handle_player_event(&self, player_id: usize, event: &PlayerEvent, can_gc: CanGc) {
// Ignore the asynchronous event from previous player.
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.

So now we have generation id, request id, and player id. Fun!

@jdm jdm added this pull request to the merge queue Jun 2, 2025
Merged via the queue into servo:main with commit 8d086b9 Jun 2, 2025
22 checks passed
@tharkum tharkum deleted the htmlmediaelement-fetch-request-race-on-seek-event branch June 3, 2025 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants