Skip to content

MEDIA_ERR_ABORTED after restore page with media from bfcache - #1722

Open
pgorszkowski-igalia wants to merge 1 commit into
wpe-2.46from
pgorszkowski/2.46/MEDIA_ERR_ABORTED-after-restore-page-with-media-from-bfcache
Open

MEDIA_ERR_ABORTED after restore page with media from bfcache#1722
pgorszkowski-igalia wants to merge 1 commit into
wpe-2.46from
pgorszkowski/2.46/MEDIA_ERR_ABORTED-after-restore-page-with-media-from-bfcache

Conversation

@pgorszkowski-igalia

@pgorszkowski-igalia pgorszkowski-igalia commented Aug 25, 2026

Copy link
Copy Markdown

https://bugs.webkit.org/show_bug.cgi?id=319665

Reviewed by Alicia Boya Garcia and Nikolas Zimmermann.

This bundles three related fixes uncovered while restoring pages containing a media element (e.g.: "video") from the back/forward cache:

  1. HTMLMediaElement::suspend() destroyed the media player on entering the cache (via stopWithoutDestroyingMediaPlayer() -> userCancelledLoad()), so a restored page reported video.error.code == MEDIA_ERR_ABORTED. userCancelledLoad() now takes a destroyMediaPlayer flag, false for the BackForwardCache suspension path, so it early-returns instead of tearing the player down.

  2. A video that was playing before being cached did not resume playing after restore. HTMLMediaElement::resume() schedules an updatePlayState() task while the media session is still interrupted. mayResumePlayback() now also retries updatePlayState() when the element isn't paused.

  3. A video that was paused before being cached showed a blank/white frame instead of its last decoded frame after restore. Restoring a page rebuilds the render tree, so CoordinatedPlatformLayerBufferProxy gets attached to a brand new CoordinatedPlatformLayer with no buffer of its own, and a paused GStreamer pipeline has no new sample to push. The proxy now invokes a layer-attached callback whenever a new non-null layer is attached, which MediaPlayerPrivateGStreamer uses to re-deliver its current sample so the new layer isn't left blank.

Also fixes a related macOS-only assert crash (ASSERTION FAILED: m_mediaSessions.contains(identifier) in MediaUsageManagerCocoa:: updateMediaUsage): the UI process unconditionally clears its usage-tracking map on every navigation commit, including a back/forward cache restore, but MediaElementSession kept believing it had already added its session across the whole suspend/resume cycle. mediaUsageManagerSessionWillBeSuspended() resets that flag on suspend so the next update re-adds the session first.

A LayoutTest for the playing-before-caching scenario (fix #2): it plays a video, waits for the playing event, navigates away and back via history.back(), and asserts the video is still not paused and currentTime has advanced after restore.

Added a LayoutTest based on media/restore-from-page-cache.html for a video that was paused before entering the cache, as a real pixel/screenshot comparison to catch the blank-frame regression from fix #3.

Tests: media/restore-from-page-cache-paused.html
media/restore-from-page-cache-playing.html

  • LayoutTests/media/restore-from-page-cache-paused-expected.png: Added.
  • LayoutTests/media/restore-from-page-cache-paused-expected.txt: Added.
  • LayoutTests/media/restore-from-page-cache-paused.html: Added.
  • LayoutTests/media/restore-from-page-cache-playing-expected.txt: Added.
  • LayoutTests/media/restore-from-page-cache-playing.html: Added.
  • LayoutTests/platform/ios/TestExpectations:
  • Source/WebCore/html/HTMLMediaElement.cpp: (WebCore::HTMLMediaElement::userCancelledLoad):
    (WebCore::HTMLMediaElement::stopWithoutDestroyingMediaPlayer): (WebCore::HTMLMediaElement::suspend):
    (WebCore::HTMLMediaElement::mayResumePlayback):
  • Source/WebCore/html/HTMLMediaElement.h:
  • Source/WebCore/html/MediaElementSession.cpp: (WebCore::MediaElementSession::mediaUsageManagerSessionWillBeSuspended):
  • Source/WebCore/html/MediaElementSession.h:
  • Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp: (WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer): (WebCore::MediaPlayerPrivateGStreamer::pushTextureToCompositor): (WebCore::MediaPlayerPrivateGStreamer::triggerRepaint):
  • Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
  • Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferProxy.cpp: (WebCore::CoordinatedPlatformLayerBufferProxy::CoordinatedPlatformLayerBufferProxy): (WebCore::CoordinatedPlatformLayerBufferProxy::create): (WebCore::CoordinatedPlatformLayerBufferProxy::setTargetLayer):
  • Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferProxy.h:

Canonical link: https://commits.webkit.org/319472@main

3a0c494

Build-Tests Layout-Tests
✅ 🛠 wpe-246-amd64-build ✅ 🧪 wpe-246-amd64-layout
✅ 🛠 wpe-246-arm32-build ✅ 🧪 wpe-246-arm32-layout

https://bugs.webkit.org/show_bug.cgi?id=319665

Reviewed by Alicia Boya Garcia and Nikolas Zimmermann.

This bundles three related fixes uncovered while restoring pages containing
a media element (e.g.: "video") from the back/forward cache:

1. HTMLMediaElement::suspend() destroyed the media player on entering the
   cache (via stopWithoutDestroyingMediaPlayer() -> userCancelledLoad()),
   so a restored page reported video.error.code == MEDIA_ERR_ABORTED.
   userCancelledLoad() now takes a destroyMediaPlayer flag, false for the
   BackForwardCache suspension path, so it early-returns instead of tearing
   the player down.

2. A <video> that was playing before being cached did not resume playing
   after restore. HTMLMediaElement::resume() schedules an updatePlayState()
   task while the media session is still interrupted. mayResumePlayback()
   now also retries updatePlayState() when the element isn't paused.

3. A <video> that was paused before being cached showed a blank/white
   frame instead of its last decoded frame after restore. Restoring a page
   rebuilds the render tree, so CoordinatedPlatformLayerBufferProxy gets
   attached to a brand new CoordinatedPlatformLayer with no buffer of its
   own, and a paused GStreamer pipeline has no new sample to push. The
   proxy now invokes a layer-attached callback whenever a new non-null
   layer is attached, which MediaPlayerPrivateGStreamer uses to re-deliver
   its current sample so the new layer isn't left blank.

Also fixes a related macOS-only assert crash (ASSERTION FAILED:
m_mediaSessions.contains(identifier) in MediaUsageManagerCocoa::
updateMediaUsage): the UI process unconditionally clears its usage-tracking
map on every navigation commit, including a back/forward cache restore, but
MediaElementSession kept believing it had already added its session across
the whole suspend/resume cycle. mediaUsageManagerSessionWillBeSuspended()
resets that flag on suspend so the next update re-adds the session first.

A LayoutTest for the playing-before-caching scenario (fix #2):
it plays a video, waits for the playing event, navigates away and
back via history.back(), and asserts the video is still not paused and
currentTime has advanced after restore.

Added a LayoutTest based on media/restore-from-page-cache.html for a video
that was paused before entering the cache, as a real pixel/screenshot
comparison to catch the blank-frame regression from fix #3.

Tests: media/restore-from-page-cache-paused.html
       media/restore-from-page-cache-playing.html
* LayoutTests/media/restore-from-page-cache-paused-expected.png: Added.
* LayoutTests/media/restore-from-page-cache-paused-expected.txt: Added.
* LayoutTests/media/restore-from-page-cache-paused.html: Added.
* LayoutTests/media/restore-from-page-cache-playing-expected.txt: Added.
* LayoutTests/media/restore-from-page-cache-playing.html: Added.
* LayoutTests/platform/ios/TestExpectations:
* Source/WebCore/html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::userCancelledLoad):
(WebCore::HTMLMediaElement::stopWithoutDestroyingMediaPlayer):
(WebCore::HTMLMediaElement::suspend):
(WebCore::HTMLMediaElement::mayResumePlayback):
* Source/WebCore/html/HTMLMediaElement.h:
* Source/WebCore/html/MediaElementSession.cpp:
(WebCore::MediaElementSession::mediaUsageManagerSessionWillBeSuspended):
* Source/WebCore/html/MediaElementSession.h:
* Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer):
(WebCore::MediaPlayerPrivateGStreamer::pushTextureToCompositor):
(WebCore::MediaPlayerPrivateGStreamer::triggerRepaint):
* Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferProxy.cpp:
(WebCore::CoordinatedPlatformLayerBufferProxy::CoordinatedPlatformLayerBufferProxy):
(WebCore::CoordinatedPlatformLayerBufferProxy::create):
(WebCore::CoordinatedPlatformLayerBufferProxy::setTargetLayer):
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferProxy.h:

Canonical link: https://commits.webkit.org/319472@main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

Integrate NodeJS to WPE Certificates and websockets

2 participants