Commit be3069fb authored by Nicolás Peña Moreno's avatar Nicolás Peña Moreno Committed by Commit Bot

[LargestContentfulPaint] Ignore full viewport images

Bug: 1133883
Change-Id: Id4aac54f834ca7bcf017e8afbbaa4a151d8492b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2441732
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarAnnie Sullivan <sullivan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812992}
parent 7c287415
......@@ -137,6 +137,7 @@ ImageRecord* ImagePaintTimingDetector::UpdateCandidate() {
void ImagePaintTimingDetector::OnPaintFinished() {
frame_index_++;
viewport_size_ = base::nullopt;
if (need_update_timing_at_frame_end_) {
need_update_timing_at_frame_end_ = false;
frame_view_->GetPaintTimingDetector()
......@@ -297,6 +298,18 @@ uint64_t ImagePaintTimingDetector::ComputeImageRectSize(
FloatRect float_visual_rect =
frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
FloatRect(image_border));
if (!viewport_size_.has_value()) {
FloatRect viewport = frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
FloatRect(frame_view_->GetScrollableArea()->VisibleContentRect()));
viewport_size_ = viewport.Size().Area();
}
// An SVG image size is computed with respect to the virtual viewport of the
// SVG, so |rect_size| can be larger than |*viewport_size| in edge cases. If
// the rect occupies the whole viewport, disregard this candidate by saying
// the size is 0.
if (rect_size >= *viewport_size_)
return 0;
rect_size = DownScaleIfIntrinsicSizeIsSmaller(
rect_size, intrinsic_size.Area(),
float_visual_rect.Width() * float_visual_rect.Height());
......
......@@ -10,6 +10,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_IMAGE_PAINT_TIMING_DETECTOR_H_
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "third_party/blink/public/web/web_widget_client.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
......@@ -292,6 +293,10 @@ class CORE_EXPORT ImagePaintTimingDetector final
void RegisterNotifySwapTime();
void ReportCandidateToTrace(ImageRecord&);
void ReportNoCandidateToTrace();
// Computes the size of an image for the purpose of LargestContentfulPaint,
// downsizing the size of images with low intrinsic size. Images that occupy
// the full viewport are special-cased and this method returns 0 for them so
// that they are not considered valid candidates.
uint64_t ComputeImageRectSize(const IntRect&,
const IntSize&,
const PropertyTreeStateOrAlias&,
......@@ -317,6 +322,11 @@ class CORE_EXPORT ImagePaintTimingDetector final
// |FindLargestPaintCandidate| occur during the paint tree walk.
bool need_update_timing_at_frame_end_ = false;
// We cache the viewport size computation to avoid performing it on every
// image. This value is reset when paint is finished and is computed if unset
// when needed. 0 means that the size has not been computed.
base::Optional<uint64_t> viewport_size_;
ImageRecordsManager records_manager_;
Member<LocalFrameView> frame_view_;
Member<PaintTimingCallbackManager> callback_manager_;
......
......@@ -8,9 +8,7 @@
<div id='my_div'></div>
<script>
async_test(t => {
if (!window.PerformanceElementTiming) {
assert_unreached("PerformanceElementTiming is not implemented");
}
assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
const remote_img = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/resources/TAOImage.py?'
+ 'origin=' + window.location.origin +'&tao=';
const valid_tao = ['wildcard', 'origin', 'multi', 'multi_wildcard', 'match_origin', 'match_wildcard'];
......
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Largest Contentful Paint: size when image overflows</title>
<!-- In this test, an image with an intrinsic size of 100 x 50 is added, but
scaled up in order to overflow the viewport. It should not be reported. -->
<body>
<style>
body {
margin: 0px;
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/largest-contentful-paint-helpers.js"></script>
<script>
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
async_test(function (t) {
assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
const beforeLoad = performance.now();
new PerformanceObserver(
t.step_func_done(function(entryList) {
assert_equals(entryList.getEntries().length, 1, 'Should have received only one entry!');
const entry = entryList.getEntries()[0];
if (entry.url)
assert_unreached('Should not have received an image entry!');
})
).observe({type: 'largest-contentful-paint', buffered: true});
// Add an image, setting width and height equal to viewport.
img = document.createElement('img');
img.src = '/images/green-100x50.png';
img.id = 'image_id';
img.width = viewportWidth * 2;
img.height = viewportHeight * 2;
img.onload = () => {
const p = document.createElement('p');
p.innerHTML = 'a';
p.style = 'position: absolute; top: 10px; left: 10px;';
document.body.appendChild(p);
}
document.body.appendChild(img);
}, 'The intersectionRect of an img element overflowing is computed correctly');
</script>
</body>
......@@ -6,9 +6,7 @@
<script src="resources/largest-contentful-paint-helpers.js"></script>
<script>
async_test(function (t) {
if (!window.PerformanceElementTiming) {
assert_unreached("PerformanceElementTiming is not implemented");
}
assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
const beforeLoad = performance.now();
new PerformanceObserver(
t.step_func_done(entryList => {
......
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Largest Contentful Paint: size when image overflows</title>
<!-- In this test, an image with an intrinsic size of 100 x 50 is added, but
scaled up in order to overflow the viewport. -->
<body>
<style>
body {
......@@ -19,10 +17,10 @@ body {
let beforeRender;
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
const imgWidth = 100;
const imgHeight = 50;
async_test(function (t) {
if (!window.PerformanceElementTiming) {
assert_unreached("PerformanceElementTiming is not implemented");
}
assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
const beforeLoad = performance.now();
new PerformanceObserver(
t.step_func_done(function(entryList) {
......@@ -30,13 +28,13 @@ body {
const entry = entryList.getEntries()[0];
const url = window.location.origin + '/images/green-100x50.png';
// To compute the size, compute the percentage of the image visible and
// scale by its natural dimensions. In this test, the image is twice
// the viewport width and twice the viewport height, so it is
// scale by its natural dimensions. In this test, the image is expanded to twice its size
// but place towards the bottom right corner of the viewport, so it is
// effectively clipped to 50% by 50% of its display size. Scaling by
// its natural width and height of 100px and 50px respectively, leads
// to a weighted size of 50 by 25.
const truncatedWidth = 100 / 2;
const truncatedHeight = 50 / 2;
const truncatedWidth = imgWidth / 2;
const truncatedHeight = imgHeight / 2;
const weightedSize = truncatedWidth * truncatedHeight;
checkImage(entry, url, 'image_id', weightedSize, beforeLoad);
})
......@@ -45,8 +43,11 @@ body {
img = document.createElement('img');
img.src = '/images/green-100x50.png';
img.id = 'image_id';
img.width = viewportWidth * 2;
img.height = viewportHeight * 2;
img.width = imgWidth * 2;
img.height = imgHeight * 2;
img.style.position = 'absolute';
img.style.left = viewportWidth - imgWidth + 'px';
img.style.top = viewportHeight - imgHeight + 'px';
document.body.appendChild(img);
}, 'The intersectionRect of an img element overflowing is computed correctly');
</script>
......
......@@ -34,7 +34,7 @@ p {
);
observer.observe({type: 'largest-contentful-paint', buffered: true});
beforeRender = performance.now();
}, 'Element with elementtiming attribute is observable.');
}, 'Text element is observable as a LargestContentfulPaint candidate.');
</script>
<p id='my_text'>This is important text! :)</p>
......
// Receives an image PerformanceElementTiming |entry| and checks |entry|'s attribute values.
// Receives an image LargestContentfulPaint |entry| and checks |entry|'s attribute values.
// The |timeLowerBound| parameter is a lower bound on the loadTime value of the entry.
// The |options| parameter may contain some string values specifying the following:
// * 'renderTimeIs0': the renderTime should be 0 (image does not pass Timing-Allow-Origin checks).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment