Add dimension source attribute to HTMLImageElement (#38152)

HTMLImageElement was missing the dimension source attribute, as step 9
from
[here](https://html.spec.whatwg.org/multipage/images.html#updating-the-source-set)
wasn't implemented. This meant that for an element like this:
```html
<picture>
  <source media="(min-width: 1000px)"
    srcset=""
    width="84" height="29">
  <img
    src=""
    width="25" height="25" alt="Wikimedia Foundation" lang="en" loading="lazy">
</picture>
```

The `width` and `height` attributes of the source tag were being
ignored. This broke stuff like these icons on the main page of
Wikipedia.

**Original:**
<img width="2252" height="229" alt="image"
src="https://github.com/user-attachments/assets/fa3fdd9d-5f91-43d4-bc9d-784b0a836d44"
/>


**Fixed:**
<img width="2252" height="229" alt="image"
src="https://github.com/user-attachments/assets/6f4ca4ae-e764-4394-ac31-9a92bbb456ab"
/>


Testing: If there isn't an existing test that covers this I'll make one,
waiting for the github runner to tell me whether this is covered and
then I'll amend the PR

---------

Signed-off-by: Leo Ring <leoring03@gmail.com>
Signed-off-by: Xiaocheng Hu <hu.xiaocheng@huawei.com>
Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
Co-authored-by: Xiaocheng Hu <hu.xiaocheng@huawei.com>
Co-authored-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
This commit is contained in:
leo030303 2025-07-24 14:40:44 +01:00 committed by GitHub
parent be38bd4636
commit 5592061474
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 15 deletions

View file

@ -162,6 +162,9 @@ pub(crate) struct HTMLImageElement {
form_owner: MutNullableDom<HTMLFormElement>, form_owner: MutNullableDom<HTMLFormElement>,
generation: Cell<u32>, generation: Cell<u32>,
source_set: DomRefCell<SourceSet>, source_set: DomRefCell<SourceSet>,
/// <https://html.spec.whatwg.org/multipage/#concept-img-dimension-attribute-source>
/// Always non-null after construction.
dimension_attribute_source: MutNullableDom<Element>,
last_selected_source: DomRefCell<Option<USVString>>, last_selected_source: DomRefCell<Option<USVString>>,
#[ignore_malloc_size_of = "promises are hard"] #[ignore_malloc_size_of = "promises are hard"]
image_decode_promises: DomRefCell<Vec<Rc<Promise>>>, image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
@ -737,9 +740,20 @@ impl HTMLImageElement {
} }
// Step 4.9 // Step 4.9
self.normalise_source_densities(&mut source_set, width); if element
.get_attribute(&ns!(), &local_name!("width"))
.is_some() ||
element
.get_attribute(&ns!(), &local_name!("height"))
.is_some()
{
self.dimension_attribute_source.set(Some(element));
}
// Step 4.10 // Step 4.10
self.normalise_source_densities(&mut source_set, width);
// Step 4.11
*self.source_set.borrow_mut() = source_set; *self.source_set.borrow_mut() = source_set;
return; return;
} }
@ -1348,6 +1362,7 @@ impl HTMLImageElement {
form_owner: Default::default(), form_owner: Default::default(),
generation: Default::default(), generation: Default::default(),
source_set: DomRefCell::new(SourceSet::new()), source_set: DomRefCell::new(SourceSet::new()),
dimension_attribute_source: Default::default(),
last_selected_source: DomRefCell::new(None), last_selected_source: DomRefCell::new(None),
image_decode_promises: DomRefCell::new(vec![]), image_decode_promises: DomRefCell::new(vec![]),
line_number: creator.return_line_number(), line_number: creator.return_line_number(),
@ -1363,14 +1378,18 @@ impl HTMLImageElement {
creator: ElementCreator, creator: ElementCreator,
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<HTMLImageElement> { ) -> DomRoot<HTMLImageElement> {
Node::reflect_node_with_proto( let image_element = Node::reflect_node_with_proto(
Box::new(HTMLImageElement::new_inherited( Box::new(HTMLImageElement::new_inherited(
local_name, prefix, document, creator, local_name, prefix, document, creator,
)), )),
document, document,
proto, proto,
can_gc, can_gc,
) );
image_element
.dimension_attribute_source
.set(Some(image_element.upcast()));
image_element
} }
pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> { pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
@ -1487,6 +1506,16 @@ impl<'dom> LayoutDom<'dom, HTMLImageElement> {
fn current_request(self) -> &'dom ImageRequest { fn current_request(self) -> &'dom ImageRequest {
unsafe { self.unsafe_get().current_request.borrow_for_layout() } unsafe { self.unsafe_get().current_request.borrow_for_layout() }
} }
#[allow(unsafe_code)]
fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
unsafe {
self.unsafe_get()
.dimension_attribute_source
.get_inner_as_layout()
.expect("dimension attribute source should be always non-null")
}
}
} }
impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> { impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
@ -1504,18 +1533,16 @@ impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
} }
fn get_width(self) -> LengthOrPercentageOrAuto { fn get_width(self) -> LengthOrPercentageOrAuto {
self.upcast::<Element>() self.dimension_attribute_source()
.get_attr_for_layout(&ns!(), &local_name!("width")) .get_attr_for_layout(&ns!(), &local_name!("width"))
.map(AttrValue::as_dimension) .map(|x| *AttrValue::from_dimension(x.to_string()).as_dimension())
.cloned()
.unwrap_or(LengthOrPercentageOrAuto::Auto) .unwrap_or(LengthOrPercentageOrAuto::Auto)
} }
fn get_height(self) -> LengthOrPercentageOrAuto { fn get_height(self) -> LengthOrPercentageOrAuto {
self.upcast::<Element>() self.dimension_attribute_source()
.get_attr_for_layout(&ns!(), &local_name!("height")) .get_attr_for_layout(&ns!(), &local_name!("height"))
.map(AttrValue::as_dimension) .map(|x| *AttrValue::from_dimension(x.to_string()).as_dimension())
.cloned()
.unwrap_or(LengthOrPercentageOrAuto::Auto) .unwrap_or(LengthOrPercentageOrAuto::Auto)
} }
} }

View file

@ -31,9 +31,3 @@
[If we only have height attribute, we should get height mapped but no aspect ratio, even if <img> has attributes.] [If we only have height attribute, we should get height mapped but no aspect ratio, even if <img> has attributes.]
expected: FAIL expected: FAIL
[Loaded picture test: <source> with width and height attributes, <img> without width and height attributes]
expected: FAIL
[Loaded picture test: Both <source> and <img> are with width and height attributes]
expected: FAIL