Skip to content

Commit

Permalink
Implement auto sizes for lazy-loaded img
Browse files Browse the repository at this point in the history
Auto Sizes allows developers to omit the sizes attribute (or set it to
sizes=auto) for lazy loaded image elements with srcset. For this use
case the image's width should be provided. The browser will use the
layout width of the image in order to select the source url from the
srcset.

Spec PR:
whatwg/html#8008

WPT tests PR:
#34427

Chrome Platform Status link:
https://chromestatus.com/feature/5191555708616704

Intent to Prototype link:
https://groups.google.com/a/chromium.org/g/blink-dev/c/AYoqvNluyeA

R=pdr

Bug: 1359051
Low-Coverage-Reason: For html_srcset_parser.cc, it should be
                     covered by `image-srcset-cache.html`
                     but there seems to be cov reporing problem.
Change-Id: I8e9bc836e9582a9209db53d55e0edc96f90499f2
  • Loading branch information
tcaptan-cr authored and chromium-wpt-export-bot committed Jun 15, 2023
1 parent 5b990f3 commit 1b67a0f
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!doctype html>
<title>Auto sizes rendering</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
<meta name="assert" content="auto sizes rendering">
<img
src="/images/green.png"
width="100px"
>
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!doctype html>
<title>img parse a sizes attribute: sizes=auto</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#parse-a-sizes-attribute">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
img { height: 10px; } /* Keep all images in the viewport, so lazy images load */
#narrow-div { width: 10px; }
#wide-div { width: 500px; }
</style>
<div id=log></div>
<script src="support/parse-a-sizes-attribute.js"></script>
<img srcset='/images/green-1x1.png?ref1 50w, /images/green-16x16.png?ref1 51w' sizes='1px' id=ref1>
<img srcset='/images/green-1x1.png?ref2 50w, /images/green-16x16.png?ref2 51w' sizes='100vw' id=ref2>
<div id='narrow-div'></div>
<div id='wide-div'></div>
<script>
"use strict";

// https://github.com/web-platform-tests/rfcs/pull/75
let async_promise_test = (promise, description) => {
async_test(test => {
promise(test)
.then(() => {test.done();})
.catch(test.step_func(error => { throw error; }));
}, description);
};

function check(imgOrPicture) {
let img = imgOrPicture;
let source;
if (imgOrPicture.localName === 'picture') {
source = imgOrPicture.firstChild;
img = imgOrPicture.lastChild;
}
const ref = document.getElementById(img.dataset.ref);
async_promise_test(async (t) => {
let expect = ref.currentSrc;
if (expect) {
expect = expect.split('?')[0];
}
if (expect === '' || expect === null || expect === undefined) {
assert_unreached('ref currentSrc was ' + format_value(expect));
}
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
t.step(() => {
let got = img.currentSrc;
assert_greater_than(got.indexOf('?'), -1, 'expected a "?" in currentSrc');
got = got.split('?')[0];
assert_equals(got, expect);
})
}, imgOrPicture.outerHTML);
}

const tests = [
// Smoke tests
{sizes: '1px', 'data-ref': 'ref1'},
{sizes: '', 'data-ref': 'ref2'},

// Actual tests
{loading: 'lazy', style: 'max-width: 10px', 'data-ref': 'ref2'}, // no specified width -> 0 -> 100vw
{loading: 'lazy', sizes: 'auto', width: '0', 'data-ref': 'ref2'}, // 0 -> 100vw
{loading: 'lazy', sizes: 'auto', style: 'width: 0px', 'data-ref': 'ref2'}, // 0 -> 100vw
{loading: 'lazy', sizes: 'auto', 'data-ref': 'ref2'}, // no specified width -> 0 -> 100vw
];

function test_img(obj, i) {
const img = document.createElement('img');
let parent = document.body;
for (const attr in obj) {
if (attr === 'parent') {
parent = document.getElementById(obj.parent);
} else {
img.setAttribute(attr, obj[attr]);
}
}
img.srcset = `/images/green-1x1.png?img${i} 1w, /images/green-16x16.png?img${i} 16w`
parent.appendChild(img);
check(img);
}

function test_picture(obj, i) {
const picture = document.createElement('picture');
const source = document.createElement('source');
const img = document.createElement('img');
let parent = document.body;
for (const attr in obj) {
switch (attr) {
case 'parent':
parent = document.getElementById(obj.parent);
break;
case 'sizes':
case 'type':
case 'media':
source.setAttribute(attr, obj[attr]);
break;
default:
img.setAttribute(attr, obj[attr]);
break;
}
}
source.srcset = `/images/green-1x1.png?picture${i} 1w, /images/green-16x16.png?picture${i} 16w`;
picture.appendChild(source);
picture.appendChild(img);
parent.appendChild(picture);
check(picture);
}

onload = () => {
let i = 0;
for (const obj of tests) {
i++;
test_img(obj, i);
test_picture(obj, i);
}
done();
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html class="reftest-wait">
<title>Auto sizes rendering</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
<link rel="match" href="reference/sizes-auto-rendering-ref.html">
<script src="/common/rendering-utils.js"></script>
<script src="/common/reftest-wait.js"></script>
<meta name="assert" content="auto sizes rendering">
<img
id="testImg"
loading="lazy"
srcset="
/images/red.png 50w,
/images/green.png 100w
"
sizes="auto"
width="100px"
>
<script>
function imageLoaded() {
waitForAtLeastOneFrame().then(takeScreenshot);
}

testImg.addEventListener('load', imageLoaded);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html class="reftest-wait">
<title>Auto sizes rendering</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
<link rel="match" href="reference/sizes-auto-rendering-ref.html">
<script src="/common/rendering-utils.js"></script>
<script src="/common/reftest-wait.js"></script>
<meta name="assert" content="auto sizes rendering">
<img
id="testImg"
loading="lazy"
srcset="
/images/green.png 100w,
/images/red.png 200w
"
sizes="auto"
width="100px"
>
<script>
function imageLoaded() {
waitForAtLeastOneFrame().then(takeScreenshot);
}

testImg.addEventListener('load', imageLoaded);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!doctype html>
<html class="reftest-wait">
<title>Auto sizes dynamic rendering</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
<link rel="match" href="reference/sizes-auto-rendering-ref.html">
<script src="/common/rendering-utils.js"></script>
<script src="/common/reftest-wait.js"></script>
<meta name="assert" content="auto sizes dynamic rendering">
<img
id="testImg"
loading="lazy"
srcset="
/images/fail.gif 200w,
/images/green.png 100w,
/images/red.png 50w
"
sizes="auto"
width="50px"
>
<script>
function secondImageLoaded() {
waitForAtLeastOneFrame().then(takeScreenshot);
}

function firstImageLoaded() {
testImg.addEventListener('load', secondImageLoaded);
testImg.style.width = '100px';
}

testImg.addEventListener('load', firstImageLoaded, {once: true});
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html class="reftest-wait">
<title>Auto sizes rendering</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
<link rel="match" href="reference/sizes-auto-rendering-ref.html">
<script src="/common/rendering-utils.js"></script>
<script src="/common/reftest-wait.js"></script>
<meta name="assert" content="auto sizes rendering">
<img
id="testImg"
loading="lazy"
srcset="
/images/fail.gif 50w,
/images/green.png 100w,
/images/red.png 200w
"
sizes="auto"
width="100px"
>
<script>
function imageLoaded() {
waitForAtLeastOneFrame().then(takeScreenshot);
}

testImg.addEventListener('load', imageLoaded);
</script>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<!doctype html>
<title>img parse a sizes attribute: sizes=auto</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#parse-a-sizes-attribute">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
Expand Down Expand Up @@ -58,19 +59,16 @@
// Smoke tests
{sizes: '1px', 'data-ref': 'ref1'},
{sizes: '', 'data-ref': 'ref2'},

// Actual tests
{loading: 'lazy', sizes: 'auto', width: '10', 'data-ref': 'ref1'},
{loading: 'lazy', sizes: 'AUTO', width: '10', 'data-ref': 'ref1'},
{loading: 'lazy', width: '10', 'data-ref': 'ref1'},
{loading: 'lazy', style: 'width: 10px', 'data-ref': 'ref1'},
{loading: 'lazy', style: 'max-width: 10px', 'data-ref': 'ref2'}, // no specified width -> 0 -> 100vw
{loading: 'lazy', style: 'width: 100%; max-width: 10px', 'data-ref': 'ref1'},
{loading: 'lazy', sizes: 'auto', width: '500', 'data-ref': 'ref2'},
{loading: 'lazy', sizes: 'auto', width: '10', style: 'visibility: hidden', 'data-ref': 'ref1'},
{loading: 'lazy', sizes: 'auto', width: '10', style: 'display: inline', hidden: '', 'data-ref': 'ref1'},
{loading: 'lazy', sizes: 'auto', width: '0', 'data-ref': 'ref2'}, // 0 -> 100vw
{loading: 'lazy', sizes: 'auto', style: 'width: 0px', 'data-ref': 'ref2'}, // 0 -> 100vw
{loading: 'lazy', sizes: 'auto', 'data-ref': 'ref2'}, // no specified width -> 0 -> 100vw
{loading: 'lazy', sizes: 'auto, 100vw', width: '10', 'data-ref': 'ref1'},
{loading: 'lazy', sizes: '100vw, auto', width: '10', 'data-ref': 'ref2'},
{loading: 'eager', sizes: 'auto', width: '10', 'data-ref': 'ref2'},
Expand Down

0 comments on commit 1b67a0f

Please sign in to comment.