Skip to content

Commit 5e22c43

Browse files
Yoav Weisschromium-wpt-export-bot
Yoav Weiss
authored andcommitted
[LCP] Add animated image support
This CL adds support for better handling of animated images in LCP: * A new attribute is exposing the first animated frame's paint time (behind a flag). * `startTime` is not changed. * The PageLoadMetrics reported for LCP are set to that first frame paint time for animated images (behind another flag). * Entries are not emitted until the image is loaded. Relevant spec issue: w3c/largest-contentful-paint#83 This Change-Id: I6bb01eacb4f200f9c032ffcfcd9a1a41126a7773 Bug: 1260953
1 parent 47f30fc commit 5e22c43

7 files changed

+141
-0
lines changed

images/anim-tao.png

460 Bytes
Loading

images/anim-tao.png.headers

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Timing-Allow-Origin: *
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta charset=utf-8>
5+
<title>Largest Contentful Paint: observe image.</title>
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
<script src="../resources/largest-contentful-paint-helpers.js"></script>
9+
</head>
10+
<body>
11+
<script>
12+
promise_test(async () => {
13+
assert_implements(window.LargestContentfulPaint,
14+
"LargestContentfulPaint is not implemented");
15+
const beforeLoad = performance.now();
16+
// 262 is the size of the animated PNG up until the first frame,
17+
// including the chunk that starts the second frame (indicating that
18+
//the first frame data is done).
19+
const url = window.location.origin +
20+
'/images/anim-gr.png?pipe=trickle(262:d1)';
21+
const entry = await load_and_observe(url);
22+
// anim-gr.png is 100 by 50.
23+
const size = 100 * 50;
24+
checkImage(entry, url, 'image_id', size, beforeLoad, ["animated"]);
25+
}, "Same origin animated image is observable and has a first frame.");
26+
</script>
27+
</body>
28+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta charset=utf-8>
5+
<title>Largest Contentful Paint: observe image.</title>
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
<script src="../resources/largest-contentful-paint-helpers.js"></script>
9+
<script src="/common/get-host-info.sub.js"></script>
10+
</head>
11+
<body>
12+
<script>
13+
promise_test(async () => {
14+
assert_implements(window.LargestContentfulPaint,
15+
"LargestContentfulPaint is not implemented");
16+
const beforeLoad = performance.now();
17+
// 262 is the size of the animated PNG up until the first frame,
18+
// including the chunk that starts the second frame (indicating that
19+
//the first frame data is done).
20+
const {REMOTE_ORIGIN} = get_host_info();
21+
const url = REMOTE_ORIGIN +
22+
'/images/anim-gr.png?pipe=trickle(262:d1)';
23+
const entry = await load_and_observe(url);
24+
// anim-gr.png is 100 by 50.
25+
const size = 100 * 50;
26+
checkImage(entry, url, 'image_id', size, beforeLoad, ["renderTimeIs0", "animated-zero"]);
27+
}, "Same origin animated image is observable and has a first frame.");
28+
</script>
29+
</body>
30+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta charset=utf-8>
5+
<title>Largest Contentful Paint: observe image.</title>
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
<script src="../resources/largest-contentful-paint-helpers.js"></script>
9+
<script src="/common/get-host-info.sub.js"></script>
10+
</head>
11+
<body>
12+
<script>
13+
promise_test(async () => {
14+
assert_implements(window.LargestContentfulPaint,
15+
"LargestContentfulPaint is not implemented");
16+
const beforeLoad = performance.now();
17+
// 262 is the size of the animated PNG up until the first frame,
18+
// including the chunk that starts the second frame (indicating that
19+
//the first frame data is done).
20+
const {REMOTE_ORIGIN} = get_host_info();
21+
const url = REMOTE_ORIGIN +
22+
'/images/anim-tao.png?pipe=trickle(262:d1)';
23+
const entry = await load_and_observe(url);
24+
// anim-gr.png is 100 by 50.
25+
const size = 100 * 50;
26+
checkImage(entry, url, 'image_id', size, beforeLoad, ["animated"]);
27+
}, "Same origin animated image is observable and has a first frame.");
28+
</script>
29+
</body>
30+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta charset=utf-8>
5+
<title>Largest Contentful Paint: observe image.</title>
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
<script src="../resources/largest-contentful-paint-helpers.js"></script>
9+
</head>
10+
<body>
11+
<script>
12+
promise_test(async () => {
13+
assert_implements(window.LargestContentfulPaint,
14+
"LargestContentfulPaint is not implemented");
15+
const beforeLoad = performance.now();
16+
// 262 is the size of the animated PNG up until the first frame,
17+
// including the chunk that starts the second frame (indicating that
18+
//the first frame data is done).
19+
const url = window.location.origin + '/images/blue.png';
20+
const entry = await load_and_observe(url);
21+
// blue.png is 133 by 106.
22+
const size = 133 * 106;
23+
checkImage(entry, url, 'image_id', size, beforeLoad, ["animated-zero"]);
24+
}, "Same origin animated image is observable and has a first frame.");
25+
</script>
26+
</body>
27+
</html>

largest-contentful-paint/resources/largest-contentful-paint-helpers.js

+24
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,28 @@ function checkImage(entry, expectedUrl, expectedID, expectedSize, timeLowerBound
3333
} else {
3434
assert_equals(entry.size, expectedSize);
3535
}
36+
if (options.includes('animated')) {
37+
assert_greater_than(entry.loadTime, entry.firstAnimatedFrameTime, 'firstAnimatedFrameTime should be smaller than loadTime');
38+
assert_less_than(entry.firstAnimatedFrameTime, 1000, 'firstAnimatedFrameTime should be smaller than the delay applied to the second frame');
39+
assert_greater_than(entry.firstAnimatedFrameTime, 0, 'firstAnimatedFrameTime should be larger than 0');
40+
}
41+
if (options.includes('animated-zero')) {
42+
assert_equals(entry.firstAnimatedFrameTime, 0, 'firstAnimatedFrameTime should be 0');
43+
}
3644
}
45+
46+
const load_and_observe = url => {
47+
return new Promise(resolve => {
48+
(new PerformanceObserver(entryList => {
49+
for (let entry of entryList.getEntries()) {
50+
if (entry.url == url) {
51+
resolve(entryList.getEntries()[0]);
52+
}
53+
}
54+
})).observe({type: 'largest-contentful-paint', buffered: true});
55+
const img = new Image();
56+
img.id = 'image_id';
57+
img.src = url;
58+
document.body.appendChild(img);
59+
});
60+
};

0 commit comments

Comments
 (0)