1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
|
<!doctype html>
<head>
<meta charset="utf-8">
<title>HTMLImageElement attributes naturalWidth, naturalHeight, width, height</title>
<!-- Note: this test asserts a different expectation from what the HTML spec
requires, as of mid-2025 when this testcase is being written. The spec
behavior doesn't appear to be web-compatible for some of the cases here,
and issue https://github.com/whatwg/html/issues/11287 is filed on
addresing that. In the meantime, this test is named with ".tentative" to
indicate that it's not authoritative. After the spec change is accepted,
we can remove the neighboring naturalWidth-naturalHeight.html test which
asserts the prior spec text's expectations, since this test covers the
same ground but with its expectations set according to the
soon-to-be-updated spec text. -->
<link rel="help" href="https://github.com/whatwg/html/issues/11287">
<link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalwidth-dev">
<link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-width">
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#density-corrected-intrinsic-width-and-height">
<link rel="help" href="https://drafts.csswg.org/css-images/#natural-dimensions">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#scroller {
/* We wrap all the test content in a scroller so that it doesn't push
* the textual test-results too far out of view.
*/
border: 1px solid black;
height: 400px;
width: max-content;
overflow: scroll;
}
#containingBlock {
/* There are a few SVG images here that size so that their margin-box fills
* their containing block width. We define a specific size here so that we
* can then check for it (minus the margins) in the "data-width" attribute.
*/
width: 740px;
}
img {
/* This styling is just cosmetic, to help visualize the images. */
border: 5px solid teal;
margin: 5px;
display: block;
}
</style>
<!-- We specify the img elements in a <template> and then clone them for
testing, so that we can dynamically generate and test several variants
of each img. -->
<template id="imgTemplates">
<!-- For each img element:
* The "data-natural-{width,height}" attributes represent the expected
values of the img element's "naturalWidth" and "naturalHeight" IDL
attributes. This test implicitly expects the "width" and "height" IDL
attributes to have those same expected values; but in cases where that's
not correct, we provide the actual expected value in the
"data-{width,height}" attributes (as distinguished from
data-natural-{width,height}).
* The "title" attribute is a description of the scenario being tested, and
it must be unique to satisfy the test harness requirements. -->
<!-- JPG images -->
<img src="resources/cat.jpg"
title="raster image"
data-natural-width="320" data-natural-height="240">
<img src="resources/cat.jpg" width="10" height="10"
title="raster image with width/height attributes"
data-natural-width="320" data-natural-height="240"
data-width="10" data-height="10"
data-not-rendered-width="10" data-not-rendered-height="10">
<!-- Use "size-comes-from-broken-image-icon" attribute to opt out of checking
img.width and img.height, because they come from the UA-dependent
size of the broken image icon: -->
<img src="non-existent.jpg"
title="non existent image, no natural dimensions"
data-natural-width="0" data-natural-height="0"
size-comes-from-broken-image-icon>
<img src="non-existent.jpg" width="10" height="10"
title="non existent image with width/height attributes, no natural dimensions"
data-natural-width="0" data-natural-height="0"
data-width="10" data-height="10"
data-not-rendered-width="10" data-not-rendered-height="10">
<!-- First group of SVG images: no viewBox, with a missing (or edge-casey, i.e.
negative or percent-valued) value for the width and/or height attr on the
root svg element in a SVG image. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"
title="SVG image, no natural dimensions"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"
width="40" height="10"
data-width="40" data-height="10"
data-not-rendered-width="40" data-not-rendered-height="10"
title="SVG image with width/height attrs, no natural dimensions"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"
width="40"
data-width="40" data-not-rendered-width="40"
title="SVG image with width attr, no natural dimensions"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"
height="10"
data-height="10" data-not-rendered-height="10"
title="SVG image with height attr, no natural dimensions"
data-natural-width="300" data-natural-height="150">
<!-- Note: percent values can't be resolved when determining natural
dimensions, so the exact percentage shouldn't matter. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='400%' height='10%'></svg>"
title="SVG image, percengage natural dimensions"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='-400%' height='-10%'></svg>"
title="SVG image, negative percengage natural dimensions"
data-natural-width="300" data-natural-height="150">
<!-- If only one attribute is present, it should show up as a natural
dimension, without influencing the other natural dimension. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60'></svg>"
title="SVG image, with natural width"
data-natural-width="60" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='60'></svg>"
title="SVG image, with natural height"
data-natural-width="300" data-natural-height="60">
<!-- If either attribute is 0 or a negative length, it should show up as a
natural dimension: of 0. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='0'></svg>"
title="SVG image, with natural width of 0"
data-natural-width="0" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='0'></svg>"
title="SVG image, with natural height of 0"
data-natural-width="300" data-natural-height="0">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='-5'></svg>"
title="SVG image, with natural width being negative"
data-natural-width="0" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='-5'></svg>"
title="SVG image, with natural height being negative"
data-natural-width="300" data-natural-height="0">
<!-- Second group of SVG images: Same as above, but now with a viewBox that grants a
3:1 aspect-ratio; whenever we know one natural dimension, that should
combine with the aspect ratio to produce the other natural dimension.
NOTE: for a few subtests here, the image ends up expanding to fill the
containing block's width, i.e. rendering at a larger size than its natural
size. In those cases, we include 'data-width' & 'data-height' attributes,
so that this test's JS can validate that img.width and img.height return
these expected larger values. (Otherwise, we expect img.width and
img.height to return the same values as img.naturalWidth and
img.naturalHeight). -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 200'></svg>"
title="SVG image, no natural dimensions, and aspect ratio from viewBox"
data-natural-width="300" data-natural-height="100"
data-width="720" data-height="240">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='400%' height='10%' viewBox='0 0 600 200'></svg>"
title="SVG image, percengage natural dimensions, and aspect ratio from viewBox"
data-natural-width="300" data-natural-height="100"
data-width="720" data-height="240">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='-400%' height='-10%' viewBox='0 0 600 200'></svg>"
title="SVG image, negative percengage natural dimensions, and aspect ratio from viewBox"
data-natural-width="300" data-natural-height="100"
data-width="720" data-height="240">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width, and aspect ratio from viewBox"
data-natural-width="60" data-natural-height="20">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='60' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural height, and aspect ratio from viewBox"
data-natural-width="180" data-natural-height="60">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='0' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width of 0, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='0' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural height of 0, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='-5' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width being negative, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='-5' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural height being negative, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
<!-- Third group of SVG images: Check a degenerate 0-sized viewBox for some of the
cases; it should have no impact. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 0 0'></svg>"
title="SVG image, no natural dimensions, viewBox with 0 width/height"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 0'></svg>"
title="SVG image, no natural dimensions, viewBox with 0 width"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 0 10'></svg>"
title="SVG image, no natural dimensions, viewBox with 0 height"
data-natural-width="300" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' viewBox='0 0 0 0'></svg>"
title="SVG image, with natural width, viewBox with 0 width/height"
data-natural-width="60" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' viewBox='0 0 10 0'></svg>"
title="SVG image, with natural width, viewBox with 0 width"
data-natural-width="60" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' viewBox='0 0 0 10'></svg>"
title="SVG image, with natural width, viewBox with 0 height"
data-natural-width="60" data-natural-height="150">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='60' viewBox='0 0 0 0'></svg>"
title="SVG image, with natural height, viewBox with 0 width/height"
data-natural-width="300" data-natural-height="60">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='60' viewBox='0 0 10 0'></svg>"
title="SVG image, with natural height, viewBox with 0 width"
data-natural-width="300" data-natural-height="60">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='60' viewBox='0 0 0 10'></svg>"
title="SVG image, with natural height, viewBox with 0 height"
data-natural-width="300" data-natural-height="60">
<!~- Final group of SVG images: we have pixel-valued width/height on the root
svg element, and possibly viewBox as well. The width and height attrs should
determine the natural dimensions, with no impact from viewBox. -->
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' height='40'></svg>"
title="SVG image, with natural width and height"
data-natural-width="60" data-natural-height="40">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='60' height='40' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width and height, and aspect ratio from viewBox"
data-natural-width="60" data-natural-height="40">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width and height of 0, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='-5' height='-5' viewBox='0 0 600 200'></svg>"
title="SVG image, with natural width and height being negative, and aspect ratio from viewBox"
data-natural-width="0" data-natural-height="0">
</template>
</head>
<body>
<div id="scroller">
<div id="containingBlock">
</div>
</div>
<!-- We generate and append all of the tested <img> elements while we're inside
the <body>, so that all of the <img> elements' "load" events will block
the window onload event: -->
<script>
setup({explicit_done:true});
// Utility function to generate a clone of imgTemplates using "srcset" and a
// given density value, with expectations adjusted per the density:
function cloneTemplateWithDensity(density) {
if (typeof(density) !== "number" || density <= 0) {
// If we get here, there's a bug in the test itself; throw an exception:
throw new Error("test error: param should be a positive number");
}
let clone = imgTemplates.content.cloneNode("true");
for (let img of clone.children) {
// Swap out "src" for "srcset":
// The srcset attribute uses a space-separated list of tokens,
// so we need to encode any spaces that are in the URI itself
// before we put it in srcset:
let srcVal = img.getAttribute("src").replaceAll(" ", "%20");
img.removeAttribute("src");
img.setAttribute("srcSet", `${srcVal} ${density}x`);
// Mention the density in the 'title' to keep the title values unique:
img.setAttribute("title",
`${img.getAttribute("title")} (with srcset/${density}x)`);
const isUsingConcreteObjectWidth = (img.dataset.naturalWidth == "300");
const isUsingConcreteObjectHeight = (img.dataset.naturalHeight == "150");
// Scale our 'data-natural-{width,height}' expectations by the density.
// But also:
// * Preserve the original 'data-natural-{width,height}' as the
// 'data-{width,height}' expectation if it's just the concrete object size
// (which doesn't actually get scaled by the density in practice when
// the image actually renders).
// * Preserve the original 'data-natural-{width,height}' as the
// 'data-not-rendered-{width,height}' expectation (if we don't already have
// one) since browsers don't do density-correction on .width and .height when
// the image is not being rendered, as discussed in
// https://github.com/whatwg/html/issues/11287#issuecomment-2923467541
for (let name in img.dataset) {
if (name.startsWith("natural")) {
let origExpectation = img.dataset[name];
// Scale our img.natural{Width,Height} expectation:
img.dataset[name] = origExpectation / density;
let isWidthAxis = (name == "naturalWidth");
let nameWithoutNatural = (isWidthAxis ? "width" : "height");
let isConcreteObjectSize =
(isWidthAxis ? isUsingConcreteObjectWidth : isUsingConcreteObjectHeight);
if (isConcreteObjectSize && !(nameWithoutNatural in img.dataset)) {
img.dataset[nameWithoutNatural] = origExpectation;
}
// Construct a string for "data-not-rendered-{width,height}" for
// whichever axis we're currently handling, and stash the
// origExpectation in there if we don't already have some expectation
// set there:
let notRenderedName = name.replace("natural", "notRendered");
if (!(notRenderedName in img.dataset)) {
img.dataset[notRenderedName] = origExpectation;
}
}
}
}
return clone;
}
// Clone and append a copy of the contents of imgTemplates, for testing:
let clone = imgTemplates.content.cloneNode("true");
containingBlock.appendChild(clone);
// Append additional clones, modified to use srcset with specified density.
// Note:
// * '1' is the trivial case; we're just testing that for completeness,
// to be sure there aren't any unexpected differences between
// <img src="..."> and <img srcset="... 1x">).
// * It's best for whatever density we test here to be something that evenly
// divides all of the image sizes in this test (so 1 and 2 are easy choices).
containingBlock.appendChild(cloneTemplateWithDensity(1));
containingBlock.appendChild(cloneTemplateWithDensity(2));
// After all the img elements have loaded (indicated by the window load event),
// we run the various tests:
onload = function() {
Array.from(document.images).forEach(img => {
const expectedNaturalWidth = parseFloat(img.dataset.naturalWidth);
const expectedNaturalHeight = parseFloat(img.dataset.naturalHeight);
test(function() {
// We expect naturalWidth to match the provided data-natural-width
// (and similar for 'height').
assert_equals(img.naturalWidth, expectedNaturalWidth, 'naturalWidth');
assert_equals(img.naturalHeight, expectedNaturalHeight, 'naturalHeight');
let shouldCheckWidthAndHeight =
!img.hasAttribute("size-comes-from-broken-image-icon");
if (shouldCheckWidthAndHeight) {
// If 'data-width' is provided, then we expect img.width to match it.
// Otherwise we expect img.width to match the 'data-natural-width'.
// (And similar for 'height'.)
const expectedWidth = 'width' in img.dataset ?
parseFloat(img.dataset.width) : expectedNaturalWidth;
const expectedHeight = 'height' in img.dataset ?
parseFloat(img.dataset.height) : expectedNaturalHeight;
assert_equals(img.width, expectedWidth, 'width');
assert_equals(img.height, expectedHeight, 'height');
}
}, `${img.title}`);
test(function() {
// Now test what we get when the img is not rendered.
// * naturalWidth and naturalHeight shouldn't change.
// * width and height should generally match naturalWidth and
// naturalHeight. (Exceptions are indicated via the
// 'data-not-rendered-{width/height} attributes).
this.add_cleanup(function() {
img.style.display = "";
});
img.style.display = "none";
img.getBoundingClientRect(); // Flush layout.
assert_equals(img.naturalWidth, expectedNaturalWidth,
'naturalWidth when not rendered');
assert_equals(img.naturalHeight, expectedNaturalHeight,
'naturalHeight when not rendered');
const expectedNotRenderedWidth = 'notRenderedWidth' in img.dataset ?
parseFloat(img.dataset.notRenderedWidth) : expectedNaturalWidth;
const expectedNotRenderedHeight = 'notRenderedHeight' in img.dataset ?
parseFloat(img.dataset.notRenderedHeight) : expectedNaturalHeight;
assert_equals(img.width, expectedNotRenderedWidth,
'width when not rendered');
assert_equals(img.height, expectedNotRenderedHeight,
'height when not rendered');
}, `${img.title} (when not rendered)`);
});
done();
};
</script>
</body>
|