Bug 1862059. Handle edge cases of svg images in multipart images. r=aosmond,gfx-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D192827
This commit is contained in:
Timothy Nikkel 2024-01-02 07:13:01 +00:00
parent ba501d058d
commit 0f39860036
5 changed files with 127 additions and 2 deletions

View File

@ -42,6 +42,43 @@ class NextPartObserver : public IProgressObserver {
mImage->RequestDecodeForSize(gfx::IntSize(0, 0),
imgIContainer::FLAG_SYNC_DECODE);
if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR) {
// We don't want to make a pending part the current part until it has had
// it's load event because when we transition from the current part to the
// next part we remove the multipart image as an observer of the current
// part and the progress tracker will send a fake load event with the
// lastpart bit set whenever an observer is removed without having the
// load complete progress. That fake load event with the lastpart bit set
// will get out of the multipart image and to the imgRequestProxy and
// further, which will make it look like we got the last part of the
// multipart image and confuse things. If we get here then we are still
// waiting to make mNextPart the current part, but we've gotten
// OnDataAvailable for the part after mNextPart. That means we must have
// gotten OnStopRequest for mNextPart; OnStopRequest calls
// OnImageDataComplete. For raster images that are part of a multipart
// image OnImageDataComplete will synchronously fire the load event
// because we synchronously perform the metadata decode in that function,
// because parts of multipart images have the transient flag set. So for
// raster images we are assured that the load event has happened by this
// point for mNextPart. For vector images there is no such luck because
// OnImageDataComplete needs to wait for the load event in the underlying
// svg document, which we can't force to happen synchronously. So we just
// send a fake load event for mNextPart. We are the only listener for it
// right now so it won't confuse anyone. When the real load event comes,
// progress tracker won't send it out because it's already in the
// progress. And nobody should be caring about load events on the current
// part of multipart images since they might be sent multiple times or
// might not be sent at all depending on timing. So this should be okay.
RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) {
Progress loadProgress =
LoadCompleteProgress(/* aLastPart = */ false, /* aError = */ false,
/* aStatus = */ NS_OK);
tracker->SyncNotifyProgress(loadProgress | FLAG_SIZE_AVAILABLE);
}
}
// RequestDecodeForSize() should've sent synchronous notifications that
// would have caused us to call FinishObserving() (and null out mImage)
// already. If for some reason it didn't, we should do so here.
@ -57,9 +94,26 @@ class NextPartObserver : public IProgressObserver {
return;
}
if (aType == imgINotificationObserver::FRAME_COMPLETE) {
FinishObserving();
if (aType != imgINotificationObserver::FRAME_COMPLETE) {
return;
}
if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR) {
RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) {
// Vector images can send frame complete during loading (any mutation in
// the underlying svg doc will call OnRenderingChange which sends frame
// complete). Whereas raster images can only send frame complete before
// the load event if there has been something to trigger a decode, but
// we do not request a decode. So we will ignore this for vector images
// so as to enforce the invariant described above in
// BlockUntilDecodedAndFinishObserving that the current part always has
// the load complete progress.
return;
}
}
FinishObserving();
}
virtual void OnLoadComplete(bool aLastPart) override {
@ -84,6 +138,15 @@ class NextPartObserver : public IProgressObserver {
RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
if (tracker->GetProgress() & FLAG_HAS_ERROR) {
FinishObserving();
return;
}
if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR &&
(tracker->GetProgress() & FLAG_FRAME_COMPLETE)) {
// It's a vector image that we may have already got a frame complete
// before load that we ignored (see Notify above). Now that we have the
// load event too, we make this part current.
FinishObserving();
}
}

View File

@ -0,0 +1,15 @@
<html class="reftest-wait">
<img src="1862059-1.svg"></img>
<script>
let numTimes = 15;
function doTest() {
numTimes--;
if (numTimes > 0) {
window.requestAnimationFrame(doTest);
} else {
document.documentElement.className = "";
}
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

View File

@ -0,0 +1,43 @@
--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>--BOUNDARYOMG
Content-Type: image/svg+xml; charset=utf-8
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" fill="#F00"></svg>

View File

@ -0,0 +1,3 @@
HTTP 200 OK
Content-Type: multipart/x-mixed-replace;boundary=BOUNDARYOMG
Cache-Control: no-cache

View File

@ -80,3 +80,4 @@ load 1814774.avif
load 1817108.avif
load 1848717-1.avif
load 1852649.webp
pref(widget.windows.window_occlusion_tracking.enabled,false) HTTP load 1862059-1.html