Open Bug 1935269 Opened 3 months ago Updated 26 days ago

img.naturalWidth and naturalHeight return 0 for dimensionless SVG images

Categories

(Core :: Layout: Images, Video, and HTML Frames, defect)

defect

Tracking

()

People

(Reporter: dholbert, Unassigned)

References

(Blocks 2 open bugs)

Details

(Keywords: webcompat:platform-bug)

Attachments

(4 files)

Attached file testcase 1

STR:

  1. 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.

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

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.

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.naturalHeight

These 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).

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 and naturalHeight 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?

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.

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);

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.

See Also: → 1906145

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.

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

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.

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

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...

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

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.

Severity: -- → S4

(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.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: