Bug 1800979 - Mark CSS image value loads as not cancelable. r=tnikkel

Yes, the test took 15 times as much as the fix.

Differential Revision: https://phabricator.services.mozilla.com/D162474
This commit is contained in:
Emilio Cobos Álvarez 2022-11-20 00:22:20 +00:00
parent 6c0098d405
commit d05b1727e9
5 changed files with 89 additions and 2 deletions

View File

@ -22,7 +22,6 @@
using namespace mozilla;
using namespace mozilla::image;
using mozilla::dom::Document;
// The split of imgRequestProxy and imgRequestProxyStatic means that
// certain overridden functions need to be usable in the destructor.
@ -109,6 +108,7 @@ imgRequestProxy::imgRequestProxy()
mLoadFlags(nsIRequest::LOAD_NORMAL),
mLockCount(0),
mAnimationConsumers(0),
mCancelable(true),
mCanceled(false),
mIsInLoadGroup(false),
mForceDispatchLoadGroup(false),
@ -462,12 +462,21 @@ NS_IMETHODIMP imgRequestProxy::CancelWithReason(nsresult aStatus,
return CancelWithReasonImpl(aStatus, aReason);
}
void imgRequestProxy::SetCancelable(bool aCancelable) {
MOZ_ASSERT(NS_IsMainThread());
mCancelable = aCancelable;
}
NS_IMETHODIMP
imgRequestProxy::Cancel(nsresult status) {
if (mCanceled) {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(!mCancelable)) {
return NS_ERROR_FAILURE;
}
LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
mCanceled = true;
@ -494,6 +503,13 @@ imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(!mCancelable)) {
MOZ_ASSERT(mCancelable,
"Shouldn't try to cancel non-cancelable requests via "
"CancelAndForgetObserver");
return NS_ERROR_FAILURE;
}
LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
mCanceled = true;

View File

@ -111,6 +111,10 @@ class imgRequestProxy : public mozilla::PreloaderBase,
void MarkValidating();
void ClearValidating();
// Flags this image load as not cancelable temporarily. This is needed so that
// stylesheets can be shared across documents properly, see bug 1800979.
void SetCancelable(bool);
already_AddRefed<nsIEventTarget> GetEventTarget() const override;
// Removes all animation consumers that were created with
@ -215,6 +219,7 @@ class imgRequestProxy : public mozilla::PreloaderBase,
nsLoadFlags mLoadFlags;
uint32_t mLockCount;
uint32_t mAnimationConsumers;
bool mCancelable : 1;
bool mCanceled : 1;
bool mIsInLoadGroup : 1;
bool mForceDispatchLoadGroup : 1;

View File

@ -85,7 +85,13 @@ void ImageLoader::Init() {
/* static */
void ImageLoader::Shutdown() {
for (const auto& entry : *sImages) {
entry.GetKey()->CancelAndForgetObserver(NS_BINDING_ABORTED);
imgIRequest* imgRequest = entry.GetKey();
// All the images we put in sImages are imgRequestProxy, see LoadImage, but
// it's non-trivial to make the hash table to use that without changing a
// lot of other code.
auto* req = static_cast<imgRequestProxy*>(imgRequest);
req->SetCancelable(true);
req->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
sImages = nullptr;
@ -444,6 +450,10 @@ already_AddRefed<imgRequestProxy> ImageLoader::LoadImage(
if (NS_FAILED(rv) || !request) {
return nullptr;
}
// This image could be shared across documents, so its load cannot be
// canceled, see bug 1800979.
request->SetCancelable(false);
sImages->GetOrInsertNew(request);
return request.forget();
}
@ -467,6 +477,8 @@ void ImageLoader::UnloadImage(imgRequestProxy* aImage) {
return;
}
// Now we want to really cancel the request.
aImage->SetCancelable(true);
aImage->CancelAndForgetObserver(NS_BINDING_ABORTED);
MOZ_DIAGNOSTIC_ASSERT(lookup.Data()->mImageLoaders.IsEmpty(),
"Shouldn't be keeping references to any loader "

View File

@ -0,0 +1,5 @@
<!doctype html>
<title>CSS Test Reference</title>
<style>
:root { background-color: lime }
</style>

View File

@ -0,0 +1,49 @@
<!doctype html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>CSS Test: Canceled load in another page doesn't affect new stylesheet</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#background-image">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1800979">
<link rel="match" href="background-image-shared-stylesheet-ref.html">
<iframe></iframe>
<script>
const IMAGE_URI = new URL("/images/green.png", location.href).href + "?pipe=trickle(d2)&" + Math.random();
const SHEET_URI = "data:text/css," + encodeURI(`:root{background-image: url('${IMAGE_URI}');}`);
const iframe = document.querySelector("iframe");
function willNavigate() {
// The child page has already loaded the stylesheet (guaranteed by the <script> rules)
// and is about to navigate away. Trigger our stylesheet load and thus image
// load when it does. The background-image should still apply to us.
iframe.addEventListener("load", function() {
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = SHEET_URI;
link.onload = function() {
iframe.remove();
// We need to also make sure that the image has actually finished to load,
// so that the reftest screenshot can be taken.
const image = new Image();
image.onload = function() {
document.documentElement.className = "";
};
image.src = IMAGE_URI;
};
document.head.appendChild(link);
}, { once: true });
}
onload = function() {
document.querySelector("iframe").srcdoc = `
<!doctype html>
<link rel="stylesheet" href="${SHEET_URI}">
<script>
document.documentElement.getBoundingClientRect(); // Should trigger the image load.
parent.willNavigate();
window.location = "/css/reference/blank.html";
</` + `script>
`;
};
</script>