img.naturalWidth and naturalHeight return 0 for dimensionless SVG images (and img.width and height return 0 for similar reasons, if such an image isn't being rendered)
Categories
(Core :: Layout: Images, Video, and HTML Frames, defect)
Tracking
()
Tracking | Status | |
---|---|---|
firefox140 | --- | fixed |
People
(Reporter: dholbert, Assigned: dholbert)
References
Details
(Keywords: spec-needed, webcompat:platform-bug)
User Story
platform-scheduled:2025-07-15
Attachments
(7 files)
STR:
- Load attached testcase (which has an <img> pointing at a trivial dimensionless SVG data-URI document)
EXPECTED RESULTS:
The testcase should report img naturalWidth x naturalHeight: 300 x 150
ACTUAL RESULTS:
The testcase reports img naturalWidth x naturalHeight: 0 x 0
Chromium and WebKit give EXPECTED RESULTS.
Firefox gives ACTUAL RESULTS.
Note that we do report 300x150 for the img.width
and height
attributes, but we're returning 0
from naturalWidth
and naturalHeight
.
It looks like these attributes are all defined here in the HTML spec, in a non-normative section:
https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-width-dev
https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalwidth-dev
In this case it seems Chrome/Safari are reporting the actual rendered width/height as the naturalWidth (because there is no actual "natural width" or "natural height" for this dimensionless image). I'm not yet finding spec justification for that, but there's at least one site that seems to rely on the Chrome/Safari behavior when computing an aspect ratio to kick off their sizing logic (which results in 0/0 = NaN in Firefox); see bug 1933708.
Assignee | ||
Updated•9 months ago
|
Assignee | ||
Comment 1•9 months ago
•
|
||
Using this more legacy-friendly testcase, I confirmed that this behavior goes back quite a long ways in Firefox; we show the same behavior[1] going back at least to NIghtly 2011-12-01 (Firefox 11 timeframe). So this has probably been the behavior ever since we implemented SVG-as-an-image, I'd guess.
[1] that same behavior being:
img width x height: 300 x 150
img naturalWidth x naturalHeight: 0 x 0
Assignee | ||
Comment 2•9 months ago
|
||
And Chromium's behavior goes back a long ways too; Chrome 37 (the oldest that BrowserStack lets me test on Win11) matches current Chrome in returning 300x150 for the naturalWidth x naturalHeight here.
Comment 3•9 months ago
|
||
See bug 1607081. I think this is just a duplicate of that.
Having said that the HTML spec says this...
https://html.spec.whatwg.org/multipage/embedded-content.html#embedded-content
image.naturalWidth
image.naturalHeightThese attributes return the natural dimensions of the image, or 0 if the dimensions are not known.
We don't know the dimensions do we? So we must return 0, no?
I've always thought (like Cameron did) that bug 1607081 is invalid per the HTML specification (as is this bug).
Comment 4•8 months ago
|
||
These attributes are not just defined by the (informative) note in the spec; below that, within the main text, we see:
The IDL attributes
naturalWidth
andnaturalHeight
must return the density-corrected natural width and height of the image, in CSS pixels, if the image has density-corrected natural width and height and is available, or else 0.
This seems pretty unambiguous. A dimensionless SVG has no natural width/height, and therefore these attributes must return 0.
So according to the HTML spec, I'd agree this is clearly invalid (and the other browsers have a bug).
However, maybe this is a case where we should accept that the webkit/blink behavior is more-or-less a de facto standard for the web, and the HTML spec (and our behavior) should be modified to reflect this?
Assignee | ||
Comment 5•8 months ago
|
||
Thanks for those references.
(In reply to Jonathan Kew [:jfkthame] from comment #4)
However, maybe this is a case where we should accept that the webkit/blink behavior is more-or-less a de facto standard for the web, and the HTML spec (and our behavior) should be modified to reflect this?
Hard to know, but I suspect this may be the case, given the longstanding Chromium/WebKit behavior here (and their collective dominance, paired with the fact that there is at least some web content that accidentally depends on their behavior).
I'll post some links to the WebKit code that seems to implement their current beahvior, for reference if/when we do end up updating the spec and/or our implementation to mitigate webcompat issues here.
Assignee | ||
Comment 6•8 months ago
•
|
||
Here is WebKit's HTMLImageElement::naturalWidth()
implementation.
With a bit of guesswork about the exact codepath, I think that function ends up calling this stack of functions to reach the relevant SVG-image sizing code:
CachedImage::unclampedImageSizeForRenderer
CachedImage::imageSizeForRenderer
SVGImageCache::imageSizeForRenderer
SVGImage::size
That size
function just returns SVGImage::m_intrinsicSize
which would have gotten its value in SVGImage::containerSize
And specifically this section seems to fall back to 300x150 when setting that value:https://searchfox.org/wubkat/rev/48c752dce43162935898b93cefa254a21a5e84c5/Source/WebCore/svg/graphics/SVGImage.cpp#148-149,172-180
IntSize SVGImage::containerSize() const
{
...
FloatSize currentSize;
if (rootElement->hasIntrinsicWidth() && rootElement->hasIntrinsicHeight())
currentSize = rootElement->currentViewportSizeExcludingZoom();
else
currentSize = rootElement->currentViewBoxRect().size();
// Use the default CSS intrinsic size if the above failed.
if (currentSize.isEmpty())
return IntSize(300, 150);
Assignee | ||
Comment 7•8 months ago
•
|
||
Note that the isEmpty
function at the end of my previous comment is defined here:
https://searchfox.org/wubkat/rev/48c752dce43162935898b93cefa254a21a5e84c5/Source/WebCore/platform/graphics/FloatSize.h#77
constexpr bool isEmpty() const { return m_width <= 0 || m_height <= 0; }
So it seems like WebKit is explicitly declining to have an SVG image whose intrinsic height or width is zero, even if it's declared as such.
Assignee | ||
Comment 8•8 months ago
|
||
Chrome's behavior is a bit more subtle than WebKit's -- they do return a naturalWidth (or naturalHeight) of 0
if the image's svg
element is explicitly declared as having an 0px
width (or height).
It's only when the svg element is declared as having a percent-valued width
(or height
) that Chrome treats it as having a naturalWidth
(or naturalHeight
) that matches the fallback replaced-element size.
Assignee | ||
Comment 9•8 months ago
•
|
||
Here's a testcase to demonstrate that distinction, where Firefox/Chromium/WebKit all disagree on img naturalWidth x naturalHeight
. I've made the width
an explicit 0px
size here, vs. height an explicit 0%
size, and the length vs percentage are handled differently in Chromium:
- Firefox:
0 x 0
- Chromium:
0 x 150
- WebKit:
300 x 150
Assignee | ||
Comment 10•8 months ago
|
||
So I guess testcase 3 is showing that:
- WebKit refuses to accept that the natural width and height might actually be legitimately 0
- whereas Chrome accepts those as possible legitimate values, but still handles the "not-available/not-known" case by returning the appropriate component of the fallback 300x150 replaced-element dimensions.
- whereas Firefox accepts 0 as possible legitimate values and also handles the "not-available/not-known" case by returning 0 per spec.
Assignee | ||
Comment 11•8 months ago
|
||
Chrome's SVG-as-an-image implementation is in this file:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/svg/graphics/svg_image.cc
...and intrinsic_size_
is the relevant member-variable for the purposes of this bug. They do indeed initialize it to 300x150 in some cases, here (kDefaultWidth
by kDefaultHeight
):
intrinsic_size_ = PhysicalSize::FromSizeFFloor(blink::ConcreteObjectSize(
sizing_info, gfx::SizeF(LayoutReplaced::kDefaultWidth,
LayoutReplaced::kDefaultHeight)));
They consider this to be a bug, per a comment here:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/svg/graphics/svg_image.cc;l=706-707;drc=616d60fca655937c2b730db94fd32d37ddff3bb5
which points to this bug:
https://issues.chromium.org/issues/41357911
Assignee | ||
Comment 12•8 months ago
|
||
I dropped a comment on the Chromium bug report to see if their version of this can get retriaged.
Given that their version of the bug is much narrower than WebKit's version, and is known-to-be-a-bug, and we've got only one known compat issue associated, it seems conceivable that we can get alignment on the specced behavior that Firefox already implements here...
Assignee | ||
Comment 13•8 months ago
|
||
Here's a testcase checking a variety of width/height combinations, most of which should produce a naturalWidth or naturalHeight value of 0.
Firefox gives PASS: All subtests passed!
Chrome gives FAIL: found 24 failures. Check DevTools console for details
WebKit gives FAIL: found 60 failures. Check DevTools console for details
Assignee | ||
Comment 14•8 months ago
|
||
I filed https://bugs.webkit.org/show_bug.cgi?id=284341 on the WebKit behavior here.
Comment 15•8 months ago
|
||
So this does seem invalid (and a dupe of bug 1607081), but I'll leave it open for now; let's see if/how the other browsers respond to their bugs.
Assignee | ||
Comment 16•7 months ago
|
||
(In reply to Jonathan Kew [:jfkthame] from comment #15)
So this does seem invalid (and a dupe of bug 1607081), but I'll leave it open for now; let's see if/how the other browsers respond to their bugs.
Good news; there now seems to be a fair amount of ongoing progress on the Chromium bug which is a great sign.
I'll still leave this open since for now at least this is still a potential point of interop pain, but if Chromium's bug manages to get closed, I think we're good to close this out too.
Assignee | ||
Comment 17•5 months ago
|
||
As I just noticed in bug 1951871, this behavior-difference is also exposed via the .width
and .height
attributes for images that are not being rendered (i.e. not in the DOM), because https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-width defines those in terms of the natural {width,height}
if the image has a natural width or height (or else 0 if the image does not, as is the case here).
STR:
- Load the testcase included here, which reports the width and height IDL attributes of an
img
that's not in the DOM, whosesrc
is pointing at a dimensionless SVG image (with a 40x30, aspect-ratio just to make things non-default).
RESULTS, by browser:
0 x 0 (Firefox)
200 x 150 (Chromium)
40 x 30 (WebKit)
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Updated•5 months ago
|
Updated•5 months ago
|
Assignee | ||
Comment 18•5 months ago
|
||
[removing webcompat:blocked-resources
-- no one's actively working on this at the moment, but that's for good reasons as discussed in comment 16. If it becomes clear that we do need to make a change here (instead of engaging other browsers to align on our spec-compliant behavior, as I've been doing), we can likely get this resourced. In that case, it's likely not too complex to land a fix here; it's mostly a question of what the right change would be, since there's no consensus behavior right now between other browsers on what size to use for unsized SVG images.]
[--> Setting a tentative platform-scheduled date for early H2, since I think it's worth being sure we've got this sorted out by that point, and I think it's feasible to do so.]
Assignee | ||
Comment 19•5 months ago
•
|
||
(In reply to Daniel Holbert [:dholbert] from comment #8)
Chrome's behavior is a bit more subtle than WebKit's -- they do return a naturalWidth (or naturalHeight) of
0
if the image'ssvg
element is explicitly declared as having an0px
width (or height).
Side note - IanK mentioned in the CSSWG meeting today that Chromium has run into compat constraints where sites require this^ special handling for 0 -- i.e. there are cases where sites use an SVG image that has explicit height="0"
and/or width="0"
on the root <svg>
element, where there's a strong requirement that the image be treated as having a natural size of 0
in that axis.
So if we do change behavior here, we need to keep that constraint in mind (i.e. we don't want to match the WebKit behavior that I described in comment 10). In other words: if we add special zero-avoiding behavior here for compat purposes, it needs to be scoped to only change behavior for images that lack a length-valued height and/or width on the root svg node.
(also: added spec-needed keyword since any behavior-changes we hypothetically make here will want to be based on (or fodder-for) spec updates to reflect the new reality.)
Assignee | ||
Comment 20•5 months ago
|
||
One other observation about possible fixes here -- for the sites that are broken by this, the most important thing to get something painting is to return a nonzero value. However, also-important is for the number to be reasonably large, because it may be used as the resolution-to-which-the-image-will-be-rasterized.
Here's an extreme example to demonstrate that -- I built with a local patch to make VectorImage::GetWidth
and GetHeight
return 5
instead of 0
in their final return clauses (referenced in bug 1951871 comment 6); and that makes both icloud (bug 1923304) and findmybus.im (bug 1951871) start drawing the otherwise-missing-graphics, but they draw them extremely blurry (presumably because the SVG images get rasterized at their reported "natural size" of 5x5, and then that rasterization gets scaled to fill a larger fixed-size region on a <canvas>
.
(However, this strawman approach is fine for the other associated webcompat bug, bug 1933708, since in that case the SVG image doesn't actually ever need to be drawn; it's just a stub data URI that ultimately gets replaced with a JPG or similar.)
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Updated•5 months ago
|
Assignee | ||
Comment 21•3 months ago
|
||
I filed a spec bug https://github.com/whatwg/html/issues/11287 to change the spec on this to reflect the reality of WebCompat requirements/constraints.
Assignee | ||
Comment 22•3 months ago
|
||
Updated•3 months ago
|
Assignee | ||
Comment 23•3 months ago
|
||
Comment 24•3 months ago
|
||
Comment 25•3 months ago
|
||
bugherder |
Assignee | ||
Comment 26•3 months ago
|
||
We still show 0x0 on testcase 5 (which is bad from the perspective of bug 1923304 and bug 1951871) - I spun off followup bug 1965560 to fix that.
Updated•3 months ago
|
Description
•