LibWeb: Use the node document realm for image requests

An HTMLImageElement can be adopted across documents while an image
update is still pending. In that case the element's wrapper realm can
still be the old iframe realm, but the image request must be created and
fetched using the element's current node document.

Using the old iframe realm associates the request with the removed
iframe's document-owned image request cache, so the adopted image can
fail to complete in the parent document. Use document().realm() when
creating/updating/fetching image requests so adopted images continue
loading in their new document.

Add a regression for an image created in an iframe, then adopted into
the parent after the iframe is removed.

Chromium and Webkit do not fire a load event for the test, but Firefox
does. All load the image, where we previously did not.

See: https://github.com/whatwg/html/issues/8492
This commit is contained in:
Shannon Booth 2026-05-25 11:20:04 +02:00 committed by Alexander Kalenik
parent 7e6d4b7b7b
commit 2a2b0bf91a
4 changed files with 43 additions and 11 deletions

View file

@ -723,7 +723,7 @@ void HTMLImageElement::update_the_image_data_impl(bool restart_animations, bool
m_pending_request = nullptr;
// 4. Set the current request to a new image request whose image data is that of the entry and whose state is completely available.
m_current_request = ImageRequest::create(realm(), document().page());
m_current_request = ImageRequest::create(document().realm(), document().page());
m_current_request->set_image_data(entry->image_data);
m_current_request->set_state(ImageRequest::State::CompletelyAvailable);
m_current_frame_index = 0;
@ -750,7 +750,7 @@ void HTMLImageElement::update_the_image_data_impl(bool restart_animations, bool
restart_the_animation();
// 2. Set the current request's current URL to urlString.
m_current_request->set_current_url(realm(), *url_string);
m_current_request->set_current_url(document().realm(), *url_string);
set_needs_style_update(true);
set_needs_layout_update_or_repaint_after_image_data_change(*this, DOM::SetNeedsLayoutReason::HTMLImageElementUpdateTheImageData);
@ -808,7 +808,7 @@ after_step_7:
}
// 1. Change the current request's current URL to the empty string.
m_current_request->set_current_url(realm(), String {});
m_current_request->set_current_url(document().realm(), String {});
// 2. If all of the following conditions are true:
// - the element has a src attribute or it uses srcset or picture; and
@ -849,7 +849,7 @@ after_step_7:
}
// 1. Change the current request's current URL to selected source.
m_current_request->set_current_url(realm(), selected_source.value().url);
m_current_request->set_current_url(document().realm(), selected_source.value().url);
// 2. If maybe omit events is not set or previousURL is not equal to selected source, then fire an event named error at the img element.
if (!maybe_omit_events || previous_url != selected_source.value().url)
@ -885,8 +885,8 @@ after_step_7:
// multiple image elements (as well as CSS background-images, etc.)
// 17. Set image request to a new image request whose current URL is urlString.
auto image_request = ImageRequest::create(realm(), document().page());
image_request->set_current_url(realm(), *url_string);
auto image_request = ImageRequest::create(document().realm(), document().page());
image_request->set_current_url(document().realm(), *url_string);
// 18. If the current request's state is unavailable or broken, then set the current request to image request.
// Otherwise, set the pending request to image request.
@ -934,7 +934,7 @@ after_step_7:
if (will_lazy_load_element()) {
// 1. Set the img's lazy load resumption steps to the rest of this algorithm starting with the step labeled fetch the image.
set_lazy_load_resumption_steps([this, request, image_request]() {
image_request->fetch_image(realm(), request);
image_request->fetch_image(document().realm(), request);
});
// 2. Start intersection-observing a lazy loading element for the img element.
@ -944,7 +944,7 @@ after_step_7:
return;
}
image_request->fetch_image(realm(), request);
image_request->fetch_image(document().realm(), request);
}));
}
@ -1115,8 +1115,8 @@ void HTMLImageElement::react_to_changes_in_the_environment()
key.origin = document().origin();
// 12. ⌛ Let image request be a new image request whose current URL is urlString
auto image_request = ImageRequest::create(realm(), document().page());
image_request->set_current_url(realm(), *url_string);
auto image_request = ImageRequest::create(document().realm(), document().page());
image_request->set_current_url(document().realm(), *url_string);
// 13. ⌛ Set the element's pending request to image request.
m_pending_request = image_request;
@ -1216,7 +1216,7 @@ void HTMLImageElement::react_to_changes_in_the_environment()
});
// 5. Let response be the result of fetching request.
image_request->fetch_image(realm(), request);
image_request->fetch_image(document().realm(), request);
}
}

View file

@ -35,6 +35,7 @@ Text/input/wpt-import/webstorage/event_session_storagearea.html
Text/input/wpt-import/webstorage/storage_session_window_open.window.html
Text/input/navigation/navigation-navigate.html
Text/input/navigation/location-reload-fetch.html
Text/input/HTML/image-load-after-adoption-from-removed-iframe.html
Text/input/HTML/session-storage-event-fired-to-lazy-window.html
Text/input/HTML/parser-streams-bytes.html
Text/input/HTML/parser-streams-with-document-write.html

View file

@ -0,0 +1,2 @@
image loaded after adoption
width=120 height=120 naturalWidth=120 naturalHeight=120 complete=true

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
asyncTest(done => {
const iframe = document.createElement("iframe");
iframe.onload = () => {
const img = iframe.contentDocument.createElement("img");
// See: https://github.com/whatwg/html/issues/8492
img.onload = () => {
println("image loaded after adoption");
println(`width=${img.width} height=${img.height} naturalWidth=${img.naturalWidth} naturalHeight=${img.naturalHeight} complete=${img.complete}`);
done();
};
img.onerror = () => {
println("image errored after adoption");
done();
};
img.src = new URL("../../../Assets/120.png", document.baseURI).href;
iframe.contentDocument.body.appendChild(img);
iframe.remove();
document.body.appendChild(document.adoptNode(img));
};
document.body.appendChild(iframe);
});
</script>